26

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

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

你知道会发生什么吗?

4

9 回答 9

32

我也遇到了这个问题,实际上在这里找到了答案:

事实证明,docx 格式需要Response.End()Response.BinaryWrite.

于 2010-05-17T20:12:48.773 回答
4

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

于 2011-08-19T13:22:04.133 回答
3

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

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

于 2010-03-19T13:46:04.197 回答
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

在此示例中,问题是我声明 Byte 数组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()
于 2015-02-25T23:13:14.160 回答
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.
于 2013-05-15T18:00:51.413 回答
0

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

于 2010-03-19T13:40:42.317 回答
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();
于 2013-04-08T00:09:23.610 回答
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();          
    }
于 2015-01-09T13:55:52.823 回答
0

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

于 2021-05-18T15:34:06.050 回答