0

I've built an ActionResult to output data to a Word Document. I get no errors at compile or runtime, but when trying to open the file I get the message: 'We're sorry, We can't open filename.docx because we found a problem with its contents.'.

Here's what I'm trying to do:

public override void ExecuteResult(ControllerContext context)
{
    //Create a response stream to create and write the Excel file
    HttpContext curContext = HttpContext.Current;
    curContext.Response.Clear();
    curContext.Response.AddHeader("content-disposition", "attachment;filename=text.docx");
    curContext.Response.Charset = "";
    curContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    curContext.Response.ContentType = "application/vnd.ms-word";

    //Write the stream back to the response
    var ms = new MemoryStream();

    var repData = "<b>Mark's Test Book: With a Special Sub Title</b><br /><br /><b>Chapter: Chapter Title 1: Chapter Title sub</b><br /><br />";

    Document.CreateAndAddHtmlToWordprocessingStream(ms, repData);

    curContext.Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);

    curContext.Response.End();

}

The static method is as follows:

public static void CreateAndAddHtmlToWordprocessingStream(Stream stream, string inputBody)
{
    // Open a WordProcessingDocument based on a stream.
    WordprocessingDocument wordprocessingDocument =
        WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document);

    // Add a main document part. 
    MainDocumentPart mainPart = wordprocessingDocument.AddMainDocumentPart();

    // Create the document structure.
    mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();

    // Create the document body.
    mainPart.Document.AppendChild(new Body());

    var ms = new MemoryStream(System.Text.Encoding.Default.GetBytes("<html><head></head><body style=\"font-family:'Calibri';\">" + inputBody + "</body></html>"));

    var altChunkId = "id";
    var formatImportPart = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.Html, altChunkId);

    formatImportPart.FeedData(ms);

    var altChunk = new AltChunk { Id = altChunkId };

    mainPart.Document.Body.Append(altChunk);

    mainPart.Document.Save();

    // Close the document handle.
    wordprocessingDocument.Close();

    // Caller must close the stream.
}

I've looked at these two posts, but didn't find anything that helped:

C# return memory stream from OpenXML resulting to a corrupted word file

Streaming In Memory Word Document using OpenXML SDK w/ASP.NET results in "corrupt" document

Community
  • 1
  • 1
M Kenyon II
  • 4,136
  • 4
  • 46
  • 94
  • `ms.GetBuffer()` doesn't sound right... Usually you would do `ms.Position = 0;` and return the stream. If you want to hit a byte array first, use `ms.ToArray()`. – Mitch Jan 12 '16 at 18:57

2 Answers2

1

ms.GetBuffer() will return the automatically managed and sized buffer. This will start with the data you have written, but may contain extra \0 bytes at the end in the eventuality you continue to .Write().

To return a MemoryStream, you can use either of the following:

ms.Position = 0;
ms.CopyTo(curContext.Response.OutputStream);

or:

var msResult = ms.ToArray();
curContext.Response.OutputStream.Write(msResult, 0, msResult.Length);
Mitch
  • 21,223
  • 6
  • 63
  • 86
  • I ended up doing this: ` ms.WriteTo(curContext.Response.OutputStream);` and it worked. Thanks. – M Kenyon II Jan 12 '16 at 19:35
  • I'm glad to hear that that solved your problem, you may consider accepting it as such by clicking the green checkmark under the voting buttons. – Mitch Jan 12 '16 at 19:52
0

you could create a method like this to handle the memory stream and the filename formatting

private static void DynaGenWordDoc(string fileName, Page page, WordprocessingDocument wdoc)
{
    page.Response.ClearContent();
    page.Response.ClearHeaders();
    page.Response.ContentType = "application/vnd.ms-word";
    page.Response.AppendHeader("Content-Disposition", string.Format("attachment;filename={0}.docx", fileName));

    using (MemoryStream memoryStream = new MemoryStream())
    {
        wdoc.SaveAs(memoryStream);
        memoryStream.WriteTo(page.Response.OutputStream);
        memoryStream.Close();
    }
    page.Response.Flush();
    page.Response.End();
}
MethodMan
  • 18,625
  • 6
  • 34
  • 52