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

和自甴很熟 提交于 2020-01-22 14:38:12

问题


I have a problem with a MemoryStream from OpenXML. I succeed with opening a Word file, changing it and downloading it through the HttpResponse if I do all the steps in a single method.

But if I try to do it in two different classes (or methods) by returning the MemoryStream, I get a corrupted word file. I thought about a flushing or buffer problem but I don't find a solution.

Here is the working code :

    public void FillTemplateOpenXmlWord(HttpResponse response)
    {
        string filePath = @"c:\template.docx";
        byte[] filebytes = File.ReadAllBytes(filePath);

        using (MemoryStream stream = new MemoryStream(filebytes))
        {
            using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, true))
            {
                // do some changes
                ...
                myDoc.MainDocumentPart.Document.Save();
            }

            string docx = "docx";
            response.Clear();
            response.ClearHeaders();
            response.ClearContent();
            response.AddHeader("content-disposition", "attachment; filename=\"" + docx + ".docx\"");
            response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            response.ContentEncoding = Encoding.GetEncoding("ISO-8859-1");
            stream.Position = 0;
            stream.CopyTo(response.OutputStream);
            response.End();
        }
    }

Here is the non-working code :

    public void OpenFile(HttpResponse response)
    {
        MemoryStream stream = this.FillTemplateOpenXmlWord();

        string docx = "docx";
        response.Clear();
        response.ClearHeaders();
        response.ClearContent();
        response.AddHeader("content-disposition", "attachment; filename=\"" + docx + ".docx\"");
        response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        response.ContentEncoding = Encoding.GetEncoding("ISO-8859-1");
        stream.Position = 0;
        stream.CopyTo(response.OutputStream);
        response.End();
    }

    public MemoryStream FillTemplateOpenXmlWord()
    {
        string filePath = @"c:\template.docx";
        byte[] filebytes = File.ReadAllBytes(filePath);

        using (MemoryStream stream = new MemoryStream(filebytes))
        {
            using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, true))
            {
                // do some changes
                ...
                myDoc.MainDocumentPart.Document.Save();
            }

            return stream;
        }
    }

Any idea ?

thank you


回答1:


looks like stream is closing when you return. it is in a using block. wouldn't that close the memory stream as soon as the filltemplate procedure ends?




回答2:


Here's what I'm using for generating OpenXML files from memory stream. In this case it makes XLSX file from template on server, but it should be similar for other OpenXml formats.

Controller action:

public class ExportController : Controller
{
    public FileResult Project(int id)
    {
        var model = SomeDateModel.Load(id); 
        ProjectExport export = new ProjectExport();
        var excelBytes = export.Export(model);
        FileResult fr = new FileContentResult(excelBytes, "application/vnd.ms-excel")
        {
            FileDownloadName = string.Format("Export_{0}_{1}.xlsx", DateTime.Now.ToString("yyMMdd"), model.Name)
        };

        return fr;
    }
}

// Helper class

public class ProjectExport
{
    private WorkbookPart workbook;
    private Worksheet ws;

    public byte[] Export(SomeDateModel model)
    {
        var template = new FileInfo(HostingEnvironment.MapPath(@"~\Export\myTemplate.xlsx"));
        byte[] templateBytes = File.ReadAllBytes(template.FullName);

        using (var templateStream = new MemoryStream())
        {
            templateStream.Write(templateBytes, 0, templateBytes.Length);
            using (var excelDoc = SpreadsheetDocument.Open(templateStream, true))
            {
                workbook = excelDoc.WorkbookPart;
                var sheet = workbook.Workbook.Descendants<Sheet>().First();

                ws = ((WorksheetPart)workbook.GetPartById(sheet.Id)).Worksheet;

                sheet.Name = model.Name;
                // Here write some other stuff for setting values in cells etc...
            }
            templateStream.Position = 0;
            var result = templateStream.ToArray();
            templateStream.Flush();

            return result;
        }
    }



回答3:


The answer posted by gashac does not describe the issues you are going to get by not calling dispose on a stream.

Not disposing a memory stream causes memory leaks (same as a "using clause").

Memory streams keeps data in memory whereas file streams keeps data on the hdd.

Solution:

Save the memory stream into a byte array, dispose the memory stream and return the bytearray.

How to return bytearray instead stream

See the following thread to return a file as a bytearray: HttpResponseMessage Content won't display PDF



来源:https://stackoverflow.com/questions/21255995/c-sharp-return-memory-stream-from-openxml-resulting-to-a-corrupted-word-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!