Подтвердить что ты не робот

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

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

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, поэтому вместо того, чтобы помещать выходной файл только в ответ, я сначала сохранил его, и файл был успешно открыт, поэтому я знаете, проблема должна быть при написании ответа.

Знаете ли вы, что может случиться?

4b9b3361

Ответ 1

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

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

Ответ 2

При хранении двоичного файла в SQL Server помните, что файл дополняется к ближайшей границе слова, поэтому у вас может быть добавлен дополнительный байт, добавленный в файл. Решение состоит в том, чтобы сохранить исходный размер файла в db при сохранении файла и использовать его для длины, которая должна быть передана функции записи объекта Stream. "Stream.Write(байты(), 0, длина)". Это ТОЛЬКО надежный способ получения правильного размера файла, что очень важно для Office 2007 и файлов вверх, которые не позволяют добавлять лишние символы в конец (большинство других типов файлов, таких как jpg, не волнует).

Ответ 3

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

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

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

        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

Если вы используете подход выше, который использует 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.

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

Ответ 6

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

Ответ 7

Взгляните на это: Написание 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();

Ответ 8

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