Skip to content

Serves a generated docx from the server

Olivier Nizet edited this page Jun 13, 2017 · 1 revision

Delivering a file from a web server seems easily but we can quickly fall in some traps due to the different behaviour of the browsers. Keep in mind also your farm administrator can set up some settings to force the file to either be downloaded or to be displayed inside the browser. This is particular true for Pdf where many browsers now natively displaying them in a tab.

You can find below a small snippet to generate a DOCX file and deliver it to the client. For the purpose of this example, we use a basic handler (.ashx):

Classic ASP.Net

public class GenerateDocument : IHttpHandler
{
     public void ProcessRequest(HttpContext context)
     {
          String documentName = "My Document";
          if (documentName.Length > 128) documentName = documentName.Substring(0, 128);
          string encodedFilename = documentName.Replace(';', ' ');

          // Avoid accent encoding bug
          if (request.Browser.Browser.Contains("IE"))
          {
                  encodedFilename = Uri.EscapeDataString(Path.GetFileNameWithoutExtension(encodedFilename)).Replace("%20", " ");
           }

          // IE cannot download an MS Office document from a website using SSL if the response
          // contains HTTP headers such as: Pragram: no-cache and/or Cache-control: no-cache,max-age=0,must-revalidate
          // http://support.microsoft.com/kb/316431/
          if (!(request.IsSecureConnection && request.Browser.Browser.Contains("IE")))
               response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);

          using (MemoryStream mstream = GenerateDocument())
          {
               context.Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
               context.Response.AppendHeader("Content-Disposition", String.Concat("attachment;filename=\"", encodedFilename, ".docx\""));

               context.Response.AddHeader("Content-Length", mstream.Length.ToString());
               mstream.WriteTo(context.Response.OutputStream);
          }
          // use IsClientConnection to avoid an HttpException
          // see http://stackoverflow.com/questions/1556073/response-flush-throws-system-web-httpexception
          if (context.Response.IsClientConnected) try { context.Response.Flush(); }
          catch (System.Web.HttpException) { }
     }

     private MemoryStream GenerateDocument()
     {
          ...
     }

     public bool IsReusable
     {
          get { return false; }
     }
}

SharePoint

public class GenerateDocument : IHttpHandler
{
     public void ProcessRequest(HttpContext context)
     {
          SPContext spcontext = SPContext.GetContext(context);
          SPWebApplication webApplication = spcontext.Site.WebApplication;

          String documentName = "My Document";
          if (documentName.Length > 128) documentName = documentName.Substring(0, 128);
          string encodedFilename = SPHttpUtility.UrlEncodeFilenameForHttpHeader(documentName.Replace(';', ' '));

          // ensure whether the farm admin force the user to download the file or to display it inside the browser.
          if (webApplication.BrowserFileHandling == SPBrowserFileHandling.Strict)
          {
               context.Response.AppendHeader("X-Content-Type-Options", "nosniff");
               context.Response.AppendHeader("X-Download-Options", "noopen");
          }

          using (MemoryStream mstream = GenerateDocument())
          {
               context.Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
               context.Response.AppendHeader("Content-Disposition", String.Concat("attachment;filename=\"", encodedFilename, ".docx\""));

               context.Response.AddHeader("Content-Length", mstream.Length.ToString());
               mstream.WriteTo(context.Response.OutputStream);
          }
          if (context.Response.IsClientConnected) try { context.Response.Flush(); }
          catch (System.Web.HttpException) { }
     }

     private MemoryStream GenerateDocument()
     {
          ...
     }

     public bool IsReusable
     {
          get { return false; }
     }
}

Content Type

Many developers complain that when they deliver DOCX file (using OpenXml SDK), their users are offered a DOC file and once downloaded, it seems corrupted. This is a classic question on StackOverflow and it can be easily answered by using the adequate content type. You should use

application/vnd.openxmlformats-officedocument.wordprocessingml.document.