从ASP.NET页面下载时为什么.docx文件被破坏?

时间:2010-03-19 13:21:45

标签: asp.net httpresponse 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文件时,所以不是仅将输出文件放在响应中,而是先保存它,然后文件成功打开,所以我知道问题应该是回应写作。

你知道会发生什么吗?

9 个答案:

答案 0 :(得分:31)

我也遇到了这个问题,实际上找到了答案:http://www.aspmessageboard.com/showthread.php?t=230778

事实证明,docx格式需要在Response.BinaryWrite之后立即使用Response.End()。

答案 1 :(得分:4)

在SQL Server中存储二进制文件时,请记住,文件填充到最近的单词boundry,因此您可能会在文件中添加一个额外的字节。解决方案是在存储文件时将原始文件大小存储在db中,并将其用于需要传递给Stream对象的write函数的长度。 “Stream.Write(bytes(),0,length)”。这是获取正确文件大小的唯一可靠方法,这对于Office 2007和up文件非常重要,它们不允许在它们的末尾添加额外的字符(大多数其他文件类型,如jpg不关心)。

答案 2 :(得分:2)

您不应该使用stream.GetBuffer(),因为它返回可能包含未使用字节的缓冲区数组。请改用stream.ToArray()。另外,你有没有尝试在写任何东西之前调用stream.Seek(0, SeekOrigin.Begin)

最诚挚的问候,
Oliver Hanappi

答案 3 :(得分:2)

对于它的价值,我也遇到了这里列出的同样问题。对我来说问题实际上是上传代码而不是下载代码:

    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。然后将该空字节与文件图像一起保存到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()

答案 4 :(得分:1)

如果使用上面使用response.Close()的方法,IE10等下载管理器会说'无法下载文件',因为字节长度与标题不匹配。请参阅文档。不要使用response.Close。 EVER。 但是,单独使用CompeteRequest动词不会关闭将字节写入输出流,因此基于XML的应用程序(如WORD 2007)会将docx视为已损坏。 在这种情况下,请将规则分解为NEVER使用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试试吗?打赌它不起作用(字节数不匹配)

答案 5 :(得分:0)

一切看起来都不错。我唯一的想法是在调用Response.Flush而不是之前尝试在你的流上调用Dispose,以防万一在刷新之前没有完全写入字节。

答案 6 :(得分:0)

看看这个:Writing MemoryStream to Response Object

我遇到了同样的问题,唯一对我有用的解决方案是:

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

答案 7 :(得分:0)

我尝试打开.docx和.xlsx文档时遇到了同样的问题。我通过定义ServerAndPrivate而不是NoCache

的可缓存性来解决问题

我的方法是调用文档:

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();          
    }

答案 8 :(得分:0)

Geoff Tanaka 的回答也适用于 Response.Writefile,而不仅仅是 binarywrite,即在摆脱 Office 文档损坏错误“Word 发现无法读取的内容”后添加 Response.End()。原来所有关于 Response.ContentType 的混乱都是不必要的,我现在可以恢复到“应用程序/八位字节流”。又一个下午,我再也回不去了。