Потоковая передача в документе Word Word с использованием OpenXML SDK с ASP.NET приводит к "поврежденному" документу

Я не могу передать текстовый документ, который я создаю "на лету", в браузер. Я постоянно получаю сообщение от Microsoft Word о том, что документ поврежден.

Когда я запускаю код через консольное приложение и вынимаю ASP.NET из изображения, документ генерируется правильно без проблем. Я считаю, что все сосредотачивается на написании файла.

Вот мой код:

using (MemoryStream mem = new MemoryStream())
{
    // Create Document
    using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
    {
        // Add a main document part. 
        MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();

        new Document(new Body()).Save(mainPart);

        Body body = mainPart.Document.Body;
        body.Append(new Paragraph(new Run(new Text("Hello World!"))));

        mainPart.Document.Save();
        // Stream it down to the browser

        // THIS IS PROBABLY THE CRUX OF THE MATTER <---
        Response.AppendHeader("Content-Disposition", "attachment;filename=HelloWorld.docx");
        Response.ContentType = "application/vnd.ms-word.document";
        mem.WriteTo(Response.OutputStream);
        Response.End();
    }
}

Я просмотрел множество ссылок, но ничего не работает. Я много людей используют MemoryStream.WriteTo а некоторые используют BinaryWrite - на данный момент я не уверен, что это правильный путь. Также я пробовал более длинный тип контента, т. application/vnd.openxmlformats-officedocument.wordprocessingml.document но не повезло.

Некоторые скриншоты - даже если вы пытаетесь восстановить, вы получаете те же "части, которые отсутствуют или недействительны"

Решение для тех, кто спотыкается на этот вопрос:

В using директивы WordProcessingDocument, вы должны позвонить:

wordDocument.Save();

Также для правильного потока MemoryStream используйте его во внешнем блоке:

Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.AppendHeader("Content-Disposition", "attachment;filename=HelloWorld.docx");
mem.Position = 0;
mem.CopyTo(Response.OutputStream);
Response.Flush();
Response.End();

enter image description hereenter image description here

Ответы

Ответ 1

Используйте CopyTo вместо этого, есть ошибка в WriteTo, которая заставляет его не записывать весь контент буфера, когда целевой поток не поддерживает запись всего за один раз.

Ответ 2

Я считаю, что ваше значение ContentType неверно; то есть для формата Word 97 - 2003. Измените его на:

application/vnd.openxmlformats-officedocument.wordprocessingml.document

и посмотрите, устраняет ли это проблему.

Ответ 3

Я скопировал и вставил ваш код и заметил, что: "wordDocument.close();" clausule отсутствовала, добавила его, и она сработала (я сделал это в Asp.NET MVC, ища действие)

Ответ 4

В качестве варианта для.NET Framework 3.5 и ниже. Эта версия фреймворка не имеет метода CopyTo в классе Stream. Поэтому метод WriteTo заменяется следующим кодом:

byte[] arr = documentStream.ToArray();
fileStream.Write(arr, 0, arr.Length);

Пример был найден по http://blogs.msdn.com/b/mcsuksoldev/archive/2010/04/09/creating-a-new-microsoft-word-document-from-a-template-using-openxml.aspx

Ответ 5

Чтобы развернуть на Rodion ответ и сопоставить переменные, используемые в вопросах, это то, что сработало для меня:

Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.AppendHeader("Content-Disposition", "attachment;filename=HelloWorld.docx");
mem.Position = 0;
byte[] arr = mem.ToArray();
Response.BinaryWrite(arr);
Response.Flush();
Response.End();

Ответ 6

Прежде всего, всегда включайте Content-Length. Если браузер не знает длину тела ответа HTTP, соединение остается открытым (keep-alive). Если нет другого способа, сохраните содержимое в файл temp (с параметром delete on close), а затем получите длину содержимого.

Во-вторых, обработка документов IN-MEM не работает для всех параметров (например, вы не можете вставить фрагмент в документ. Вы должны использовать режим файла).

Ниже приведен образец для ядра aspnet:

[HttpPost]
public async Task<ActionResult<MopedResponse>> PostAsync(IFormCollection collection)
{

    string pathTemp = Path.GetTempFileName(); // get the temp file name

    // create or process the word file

    // reopen it for serving

    FileStream merged = new FileStream(pathTemp, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose);

    System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
    {
        FileName = "parafa.docx",
        Inline = true  // false = prompt the user for downloading;  true = browser to try to show the file inline
    };
    Response.Headers.Add("Content-Disposition", cd.ToString());
    Response.Headers.Add("Content-Length", merged.Length.ToString());

    return File(merged, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "mywordFile1.docx");

}