gtu
gtu

Reputation: 966

Edit OpenXML docx template and return it as FileResult

I want to offer users of my application to download a docx file which would contain dynamic content depending on their request. I prepared a template with header, otherwise the OpenXML creation is pain in the somewhere. Now I am having trouble with editing it and returning it as FileResult in my ASP MVC application.

My plan is to read a file to MemoryStream and edit it as WordprocessingDocument and then returning MemoryStream as Byte[].

I have two problems here:

  1. If I read WordprocessingDocument directly from file without memory stream, I don't see a way to transform it to the shape FileResult() wants.
  2. If I create new WordprocessingDocument with empty MemoryStream I can simply create content and return it as File but I lack the previously prepared header with desired content.

So, how to edit a .docx template and return it as FileResult()?

Upvotes: 2

Views: 2027

Answers (2)

gtu
gtu

Reputation: 966

With the help from FortyTwo I managed to achieve my goal.

So the desired "workflow" is:

  1. copy .docx template (added headers or other things) to memory
  2. edit content of the document
  3. return this document as FileResult without saving the changes to template

So here is the code:

public FileResult EditDOCXBody()
{
    // Prepare file path
    string file = "../WordTemplates/EmptyTemplate.docx";
    String fullFilePath = HttpContext.ApplicationInstance.Server.MapPath(file);                   

    // Copy file content to MemeoryStream via byte array
    MemoryStream stream = new MemoryStream();                          
    byte[] fileBytesArray = System.IO.File.ReadAllBytes(fullFilePath);  
    stream.Write(fileBytesArray, 0, fileBytesArray.Length);             // copy file content to MemoryStream
    stream.Position = 0;                                                

    // Edit word document content
    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(stream, true))  
    {
        MainDocumentPart mainPart = wordDoc.MainDocumentPart;                
        Body body = mainPart.Document.Body;

        // add some text to body
        body.Append(new Paragraph(
                    new Run(
                        new Text("Current time is: " + DateTime.Now.ToLongTimeString()))));

        // Save changes to reflect on stream object
        mainPart.Document.Save();                
    }
    return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "servedFilename.docx");            
}

Some important notes:

  • you need to write file bytes to MemoryStream manually otherwise you get Memory stream is not expandable error. More info here
  • you have to use mainPart.Document.Save() for changes to take effect on MemoryStream
  • when returning the file, you have to use .ToArray() otherwise the returned file is corrupted

Upvotes: 2

FortyTwo
FortyTwo

Reputation: 2649

This is how you go about creating a new spreadsheet document with MemoryStream and then saving it to a Byte[]

Byte[] file;
using (MemoryStream mem = new MemoryStream())
{
    using (SpreadsheetDocument spreadsheetDocument = 
        SpreadsheetDocument.Create(mem, SpreadsheetDocumentType.Workbook))
    {
        WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
        workbookpart.Workbook = new Workbook();
        WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
        worksheetPart.Worksheet = new Worksheet(new SheetData());

        Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.
            AppendChild<Sheets>(new Sheets());

        Sheet sheet = new Sheet()
        {
            Id = spreadsheetDocument.WorkbookPart.
                GetIdOfPart(worksheetPart),
            SheetId = 1,
            Name = "aStupidSheet"
        };
        sheets.Append(sheet);
        workbookpart.Workbook.Save()
        spreadsheetDocument.Close();
    }
    file = mem.ToArray();
}

Upvotes: 3

Related Questions