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

StackOverflow https://stackoverflow.com/questions/2477564

  •  21-09-2019
  •  | 
  •  

Вопрос

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

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

Знаете ли вы, что может произойти?

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

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

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

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

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

У меня возникла та же проблема, когда я пытался открыть документы .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();          
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top