Web developers sometimes want to integrate some Open XML functionality into their web application. You may want to enable end-users to upload documents. You may want to modify the uploaded documents, or generate new documents, and you may want to enable end-users to download documents. In the screen-cast below, I present the smallest possible ASP.NET application that enables uploading an Open XML document, modifying the document using the Open XML SDK, and letting the user download a document. In the screen-cast, I show how to build the application, and I walk through the code.
Following is the ASP.NET markup for the web page in the application:
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %> <asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"> </asp:Content> <asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent"> <h2> Welcome to an ASP.NET Open XML Application! </h2> <asp:FileUpload ID="FileUploadControl" runat="server" /> <p style="line-height: .5em;"/> <asp:Label ID="lblMessage" runat="server" Text=""></asp:Label> <p style="line-height: .5em;"/> <asp:Button ID="btnUpload" runat="server" Text="Upload an Open XML Document into the Web Application" onclick="btnUpload_Click" /> <p style="line-height: .5em;"/> <asp:Button ID="btnDownload" runat="server" Text="Download the modified Open XML document" onclick="btnDownload_Click" /> </asp:Content>
Following is the C# code for that web page:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Xml; using System.Xml.Linq; using DocumentFormat.OpenXml.Packaging; namespace WebApplication1 { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnUpload_Click(object sender, EventArgs e) { if (FileUploadControl.HasFile) try { string fileNameFromUser = FileUploadControl.FileName; Session["FileNameFromUser"] = fileNameFromUser; using (MemoryStream memoryStream = new MemoryStream()) { memoryStream.Write(FileUploadControl.FileBytes, 0, FileUploadControl.FileBytes.Length); using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) { XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XElement body = wDoc.MainDocumentPart.GetXDocument() .Descendants(w + "body").FirstOrDefault(); body.AddFirst( new XElement(w + "p", new XElement(w + "r", new XElement(w + "t", "Hello world")))); wDoc.MainDocumentPart.PutXDocument(); } Session["ByteArray"] = memoryStream.ToArray(); } } catch (Exception ex) { lblMessage.Text = "ERROR: " + ex.Message.ToString(); } else { lblMessage.Text = "You have not specified a file."; } } protected void btnDownload_Click(object sender, EventArgs e) { byte[] byteArray = (byte[])(Session["ByteArray"]); Response.Clear(); Response.ContentType = "application/octet-stream"; string fileName = (string)(Session["FileNameFromUser"]); Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}", fileName)); Response.BinaryWrite(byteArray); Response.Flush(); Response.End(); } } public static class LocalExtensions { public static XDocument GetXDocument(this OpenXmlPart part) { XDocument partXDocument = part.Annotation<XDocument>(); if (partXDocument != null) return partXDocument; using (Stream partStream = part.GetStream()) { if (partStream.Length == 0) { partXDocument = new XDocument(); partXDocument.Declaration = new XDeclaration("1.0", "UTF-8", "yes"); } else using (XmlReader partXmlReader = XmlReader.Create(partStream)) partXDocument = XDocument.Load(partXmlReader); } part.AddAnnotation(partXDocument); return partXDocument; } public static void PutXDocument(this OpenXmlPart part) { XDocument partXDocument = part.GetXDocument(); if (partXDocument != null) { using (Stream partStream = part.GetStream(FileMode.Create, FileAccess.Write)) using (XmlWriter partXmlWriter = XmlWriter.Create(partStream)) partXDocument.Save(partXmlWriter); } } } }
Does document builder work with MemoryStream? What I am looking for is being able to open a docx, modify it in memory, combine it with another document, then stream it to the browser.
Hi,
DocumentBuilder does work with memory streams. How ae you going to stream to the browser? You will need to convert to html in some fashion.
-Eric
Great tutorial! Now I am going to implement the functionality of creating a test.docx file into a web application, so that when end-users click it, it will be rendered in browser. So far, I have successfully created a local test.docx file but don't know how to directly upload the stream, like what you have done for the download, instead of first creating it locally and then upload the file. Can you please elaborate on that?
Thanks for the wonderful post and video shared. It makes building ASP.NET application with open XML functionality easy for ASP.NET web developer.
Hi Eric,
Using the same code, how can I insert a table in the .docx document? Your code adds a simple text "Hello World" to your page. How can I insert a table in the document. Kindly help.
Thanks,
Gaurav
Thanks very much! A great start to my Open XML programming! Looking forward to looking at more tutorials from you! This is a much better way than to use server-side Automation of Office as outlined in this KB article: support.microsoft.com/.../257757
I am using a similar code like the above to update docxs using WCF as Windows Service spawning a new thread for each dirrerent update. The code hangs when two threads reach at the same time the closing bracket of 'using WordProcessingDocument... ', without throwing any exception. Has anybody else confronted with this situation?
Thanks