Почему файлы .docx повреждаются при загрузке со страницы ASP.NET? - PullRequest
24 голосов
/ 19 марта 2010

У меня есть следующий код для передачи пользователю вложенных страниц:

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 для открытия.

Сначала я не знал, была ли проблема в распаковке zip-файла, содержащего .docx, поэтому вместо помещения выходного файла только в ответ я сначала сохранил его, и файл успешно открылся, поэтому я знать, что проблема должна быть при написании ответа.

Знаете ли вы, что может происходить?

Ответы [ 8 ]

31 голосов
/ 18 мая 2010

Я также столкнулся с этой проблемой и на самом деле нашел ответ здесь: http://www.aspmessageboard.com/showthread.php?t=230778

Оказывается, что формат docx должен иметь Response.End () сразу после Response.BinaryWrite.

4 голосов
/ 19 августа 2011

При хранении двоичного файла в SQL Server имейте в виду, что файл дополняется до ближайшего слова, поэтому вы можете добавить в файл дополнительный байт. Решение состоит в том, чтобы сохранить исходный размер файла в БД при сохранении файла и использовать его для длины, которая должна быть передана в функцию записи объекта Stream. Msgstr "Stream.Write (bytes (), 0, length)". Это ЕДИНСТВЕННЫЙ надежный способ получения правильного размера файла, который очень важен для файлов Office 2007 и выше, которые не допускают добавления дополнительных символов в конце (большинство других типов файлов, таких как jpg, не заботятся).

2 голосов
/ 26 февраля 2015

Для чего это стоит, я также столкнулся с той же проблемой, перечисленной здесь. Для меня проблема была на самом деле с кодом загрузки, а не кодом загрузки:

    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 с размером в один байт слишком большим. Этот нулевой байт затем сохраняется вместе с изображением файла в БД и воспроизводится при загрузке. Исправленный код будет:

        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 голосов
/ 19 марта 2010

Не следует использовать stream.GetBuffer(), поскольку он возвращает буферный массив, который может содержать неиспользуемые байты. Вместо этого используйте stream.ToArray(). Кроме того, вы пытались позвонить stream.Seek(0, SeekOrigin.Begin), прежде чем что-то писать?

С наилучшими пожеланиями,
Оливер Ханаппи

1 голос
/ 15 мая 2013

Если вы используете описанный выше подход, в котором используется response.Close (), диспетчеры загрузки, такие как IE10, скажут: «Не удается загрузить файл», поскольку длины в байтах не соответствуют заголовкам. Смотрите документацию. НЕ используйте ответ. Закрыть. КОГДА-ЛИБО. Однако использование только одного глагола 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.

@ Cesar: вы используете response.Close -> вы можете попробовать это с IE 10? держу пари, что это не работает (количество байтов не совпадает)

0 голосов
/ 09 января 2015

У меня была такая же проблема, когда я пытался открыть документы .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();          
    }
0 голосов
/ 08 апреля 2013

Взгляните на это: Запись 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 голосов
/ 19 марта 2010

Все выглядит хорошо. Моя единственная идея - попытаться вызвать Dispose в вашем потоке после вызова Response.Flush, а не раньше, на случай, если байты не полностью записаны перед сбросом.

...