web-dev-qa-db-ja.com

ASP.NETページからダウンロードすると.docxファイルが破損するのはなぜですか?

ユーザーにページの添付ファイルをもたらす次のコードがあります。

private void GetFile(string package, string filename)
{
    var stream = new MemoryStream();

    try
    {
        using (ZipFile Zip = ZipFile.Read(package))
        {
            Zip[filename].Extract(stream);
        }
    }
    catch (System.Exception ex)
    {
        throw new Exception("Resources_FileNotFound", ex);
    }

    Response.ClearContent();
    Response.ClearHeaders();
    Response.ContentType = "application/unknown";

    if (filename.EndsWith(".docx"))
    {
        Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
    }

    Response.AddHeader("Content-Disposition", "attachment;filename=\"" + filename + "\"");
    Response.BinaryWrite(stream.GetBuffer());
    stream.Dispose();
    Response.Flush();
    HttpContext.Current.ApplicationInstance.CompleteRequest();
}

問題は、サポートされているすべてのファイル(jpg、gif、png、pdf、docなど)が正常に機能することですが、.docxファイルはダウンロード時に破損しており、開くにはOfficeで修正する必要があります。

最初は、問題が.docxを含むZipファイルの解凍にあるかどうかわからなかったので、出力ファイルを応答にのみ置くのではなく、最初に保存し、ファイルを正常に開いたので、問題がわかりました。応答書き込みである必要があります。

何が起きているのか知っていますか?

25

私もこの問題に遭遇し、実際にここで答えを見つけました: http://www.aspmessageboard.com/showthread.php?t=230778

Docx形式では、Response.BinaryWriteの直後にResponse.End()が必要であることがわかります。

31
Geoff Tanaka

SQL Serverにバイナリファイルを格納する場合、ファイルは最も近いWord境界に埋め込まれるので、ファイルに1バイト追加される可能性があることに注意してください。解決策は、ファイルを保存するときに元のファイルサイズをdbに保存し、それをStreamオブジェクトの書き込み関数に渡す必要がある長さに使用することです。 「Stream.Write(bytes()、0、length)」。これは、正しいファイルサイズを取得する唯一の信頼できる方法です。これは、Office 2007以降のファイルにとって非常に重要であり、余分な文字をそれらの最後に置くことを許可しません(jpgのような他のほとんどのファイルタイプは関係ありません)。

4

stream.GetBuffer()は、未使用のバイトを含む可能性のあるバッファ配列を返すため、使用しないでください。代わりにstream.ToArray()を使用してください。また、何かを書く前にstream.Seek(0, SeekOrigin.Begin)を呼び出してみましたか?

宜しくお願いします、
オリバーハナッピ

3
Oliver Hanappi

それだけの価値があるので、ここに挙げたのと同じ問題にも遭遇しました。私にとって問題は実際にはダウンロードコードではなくアップロードコードにありました:

    Public Sub ImportStream(FileStream As Stream)
        'Use this method with FileUpload.PostedFile.InputStream as a parameter, for example.
        Dim arrBuffer(FileStream.Length) As Byte
        FileStream.Seek(0, SeekOrigin.Begin)
        FileStream.Read(arrBuffer, 0, FileStream.Length)
        Me.FileImage = arrBuffer
    End Sub

この例の問題は、バイト配列arrBufferを1バイトが大きすぎると宣言することです。このヌルバイトは、ファイルイメージとともにDBに保存され、ダウンロード時に再現されます。修正されたコードは次のとおりです。

        Dim arrBuffer(FileStream.Length - 1) As Byte

また、参考のために、私のHttpResponseコードは次のとおりです。

                context.Response.Clear()
                context.Response.ClearHeaders()
                'SetContentType() is a function which looks up the correct mime type
                'and also adds and informational header about the lookup process...
                context.Response.ContentType = SetContentType(objPostedFile.FileName, context.Response)
                context.Response.AddHeader("content-disposition", "attachment;filename=" & HttpUtility.UrlPathEncode(objPostedFile.FileName))
                'For reference: Public Property FileImage As Byte()
                context.Response.BinaryWrite(objPostedFile.FileImage)
                context.Response.Flush()
2
pseudocoder

上記のresponse.Close()を使用するアプローチを使用する場合、バイト長がヘッダーと一致しないため、IE10などのダウンロードマネージャーは「ファイルをダウンロードできません」と表示します。ドキュメントを参照してください。 response.Closeは使用しないでください。これまで。ただし、CompeteRequest動詞を単独で使用しても、出力ストリームへのバイトの書き込みは停止されないため、Word 2007などのXMLベースのアプリケーションでは、docxが破損していると見なされます。この場合、ルールを破って、Response.Endを使用しないでください。次のコードは両方の問題を解決します。結果は異なる場合があります。

        '*** transfer package file memory buffer to output stream
        Response.ClearContent()
        Response.ClearHeaders()
        Response.AddHeader("content-disposition", "attachment; filename=" + NewDocFileName)
        Me.Response.ContentType = "application/vnd.ms-Word.document.12"
        Response.ContentEncoding = System.Text.Encoding.UTF8
        strDocument.Position = 0
        strDocument.WriteTo(Response.OutputStream)
        strDocument.Close()
        Response.Flush()
        'See documentation at http://blogs.msdn.com/b/aspnetue/archive/2010/05/25/response-end-response-close-and-how-customer-feedback-helps-us-improve-msdn-documentation.aspx
        HttpContext.Current.ApplicationInstance.CompleteRequest() 'This is the preferred method
        'Response.Close() 'BAD pattern. Do not use this approach, will cause 'cannot download file' in IE10 and other download managers that compare content-Header to actual byte count
        Response.End() 'BAD Pattern as well. However, CompleteRequest does not terminate sending bytes, so Word or other XML based appns will see the file as corrupted. So use this to solve it.

@Cesar:あなたはresponse.Closeを使用しています。閉じる-> IE 10?でそれを試すことができますか?機能しません(バイト数が一致しません)

1
PhilM

.docxおよび.xlsxドキュメントを開こうとすると、同じ問題が発生しました。 NoCacheではなくServerAndPrivateにキャッシュ可能性を定義することで問題を解決します

ドキュメントを呼び出す私のメソッドがあります:

public void ProcessRequest(HttpContext context)

 {


       var fi = new FileInfo(context.Request.Path);
        var mediaId = ResolveMediaIdFromName(fi.Name);
        if (mediaId == null) return;

        int mediaContentId;
        if (!int.TryParse(mediaId, out mediaContentId)) return;

        var media = _repository.GetPublicationMediaById(mediaContentId);
        if (media == null) return;

        var fileNameFull = string.Format("{0}{1}", media.Name, media.Extension);
        context.Response.Clear();
        context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}", fileNameFull));            
        context.Response.Charset = "";
        context.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
        context.Response.ContentType = media.ContentType;
        context.Response.BinaryWrite(media.Content);
        context.Response.Flush();          
        context.Response.End();          
    }
0
Stephane

すべて問題ありません。私の唯一のアイデアは、フラッシュの前にバイトが完全に書き込まれていない場合に備えて、前ではなくResponse.Flushを呼び出した後にストリームでDisposeを呼び出すことです。

0
Ray

これを見てください: MemoryStreamを応答オブジェクトに書き込む

私は同じ問題を抱えていて、私のために働いた唯一の解決策は次のとおりでした:

    Response.Clear();
    Response.ContentType = "Application/msword";
    Response.AddHeader("Content-Disposition", "attachment; filename=myfile.docx");
    Response.BinaryWrite(myMemoryStream.ToArray());
    // myMemoryStream.WriteTo(Response.OutputStream); //works too
    Response.Flush();
    Response.Close();
    Response.End();
0
playful