【问题标题】:Why are .docx files being corrupted when downloading from an ASP.NET page?从 ASP.NET 页面下载时,为什么 .docx 文件会损坏?
【发布时间】:2011-01-29 11:19:44
【问题描述】:

我有以下代码用于将页面附件带给用户:

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文件,所以我没有把输出文件只放在响应中,而是先保存,文件打开成功,所以我知道问题应该出现在响应写作中。

你知道会发生什么吗?

【问题讨论】:

  • 这让我在输出 PDF 时绊倒了。事实证明,PDF 查看器会在有效数据结束后容忍意外垃圾,并且我正在将页面的渲染 HTML 添加到我发送的每个 PDF 文件中。其他二进制文件格式可能相同,它们不关心附加到有效数据的意外数据。

标签: asp.net httpresponse docx


【解决方案1】:

如果您使用上面使用response.Close() 的方法,IE10 等下载管理器将显示“无法下载文件”,因为字节长度与标题不匹配。请参阅文档。不要使用response.Close。永远。

但是,单独使用 CompeteRequest 动词并不会关闭向输出流写入字节,因此基于 XML 的应用程序(例如 WORD 2007)会认为 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.

【讨论】:

    【解决方案2】:

    我也遇到了这个问题,居然找到了答案here

    事实证明,docx 格式需要在Response.End() 之后紧跟Response.BinaryWrite

    【讨论】:

    • 节省了我在网上拖网寻找这个的时间,谢谢!!这也是有道理的,因为该文件在流的末尾附加了一些其他位,并且最终比服务器上的稍大。
    • 添加 Response.End() 并将输出限制为原始文件大小(如下 Randall Spychalla 的解决方案)都是我解决此问题所必需的
    • Response.End 不是强制性的,但设置文件长度是为了让它知道何时完成。
    • 我正在为我的 binaryreader 使用 using 并将其包装在 try/catch 中,因此我必须将 Response.End 放在 finally 块中,否则它会在中止线程时出错。但它像宣传的那样工作!
    • 上面的链接坏了。
    【解决方案3】:

    Geoff Tanaka 的回答也适用于 Response.Writefile 而不仅仅是二进制写入,即在消除 Office 文档损坏错误“Word 发现不可读的内容”之后添加 Response.End()。原来所有的 Response.ContentType 都是不必要的,我现在可以恢复到“application/octet-stream”。又一个下午,我再也回不来了。

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        我在尝试打开 .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();          
            }
        

        【讨论】:

          【解决方案6】:

          看看这个: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】:

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

            【讨论】:

              【解决方案8】:

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

              最好的问候,
              奥利弗·哈纳皮

              【讨论】:

                【解决方案9】:

                看起来一切正常。我唯一的想法是在调用 Response.Flush 而不是之前尝试在您的流上调用 Dispose,以防在刷新之前字节未完全写入。

                【讨论】:

                • 我也这样做了,但没有成功。
                • 只是在这里胡乱猜测...尝试使用内容类型“application/octet-stream”,看看是否下载了有效文件。顺便说一句 - 当您不知道文件类型时,这可能比“应用程序/未知”更合适。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-01-30
                • 1970-01-01
                • 1970-01-01
                • 2023-04-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多