web-dev-qa-db-ja.com

OpenXML SDK w / ASP.NETを使用してメモリ内のWord文書をストリーミングすると、「破損した」文書になります

その場で作成したWord文書をブラウザにストリーミングできません。 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を試しましたが、うまくいきませんでした。

一部のスクリーンショット–回復しようとしても、同じ「パーツが欠落しているか無効」になります

この質問につまずいた人のための解決策:

usingWordProcessingDocumentディレクティブ内で、以下を呼び出す必要があります。

wordDocument.Save();

また、MemoryStreamを正しくストリーミングするには、外部のusingブロックでこれを使用します。

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

18
Ta01

代わりにCopyToを使用してください。代わりに、WriteToにバグがあり、ターゲットストリームが一度にすべてを書き込むことをサポートしていない場合、バッファのコンテンツ全体を書き込めません。

7
Magnus

.NET Framework3.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

2

コードをコピーして貼り付けたところ、: "wordDocument.close();"であることがわかりました。 clausuleが欠落していて、追加して機能しました(Asp.NET MVCでアクションをウィッティングしました)

2
el bayames

ContentTypeの値が正しくないと思います。これはWord97-2003形式用です。次のように変更します。

application/vnd.openxmlformats-officedocument.wordprocessingml.document

それで問題が解決するかどうかを確認してください。

2
Jeff Siver

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();
0
CodeMonkey

まず、常にContent-Lengthを含めます。ブラウザがhttp応答本文の長さを認識していない場合、接続は開いたままになります(キープアライブ)。他に方法がない場合は、コンテンツを一時ファイルに保存し(閉じるときに削除オプションを使用)、コンテンツの長さを取得します。

次に、ドキュメント処理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");

}
0
Mitja Gustin

(これはOpenXML SDK v2.10.0および.NetCore v2.2を使用しています)

私はこれが答えられたことを知っていますが、私は別の選択肢を捨てたかったのです。以下のようにFile()でストリームを送り返そうとすると、ドキュメントが破損するのは正しいことです。

                MemoryStream updateStream = new MemoryStream();
                wordDocument.Save();
                wordDocument.Clone(updateStream);


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

`

非常に簡単な代替/回避策は、ストリームを以下のようにbyte []に​​変換するだけで、Worddocxが機能するようになります。

                MemoryStream updateStream = new MemoryStream();
                wordDocument.Save();
                wordDocument.Clone(updateStream);


                return File(updateStream.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
0
Coy Meeks