Por que os arquivos .docx estão sendo corrompidos ao fazer o download de uma página do ASP.NET?

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

  •  21-09-2019
  •  | 
  •  

Pergunta

Eu tenho esse código a seguir para trazer anexos de página ao usuário:

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

O problema é que todos os arquivos suportados funcionam corretamente (JPG, GIF, PNG, PDF, DOC, etc.), mas os arquivos .docx, quando baixados, são corrompidos e precisam ser corrigidos pelo Office para serem abertos.

No começo, eu não sabia se o problema estava em descompactar o arquivo zip que continha o .docx; portanto, em vez de colocar o arquivo de saída apenas na resposta, salvei primeiro e o arquivo foi aberto com sucesso, então eu sei o problema deve estar na escrita de resposta.

Você sabe o que pode estar acontecendo?

Foi útil?

Solução

Eu também encontrei esse problema e realmente encontrei a resposta aqui: http://www.aspmessageboard.com/showthread.php?t=230778

Acontece que o formato DOCX precisa ter resposta.end () logo após a resposta.binaryWrite.

Outras dicas

Ao armazenar um arquivo binário no SQL Server, lembre -se de que um arquivo é preenchido para o limite do Word mais próximo, para que você possa ter um byte extra adicionado a um arquivo. A solução é armazenar o tamanho do arquivo original no banco de dados quando você armazena o arquivo e use -o para o comprimento que precisa ser transmitido para a função de gravação do objeto Stream. "Stream.Write (bytes (), 0, comprimento)". Esta é a única maneira confiável de obter o tamanho correto do arquivo, que é muito importante para os arquivos Office 2007 e UP, que não permitem que caracteres extras estejam no final deles (a maioria dos outros tipos de arquivos como os JPGs não se importam).

Você não deve usar stream.GetBuffer() Porque retorna a matriz de buffer que pode conter bytes não utilizados. Usar stream.ToArray() em vez de. Além disso, você já tentou ligar stream.Seek(0, SeekOrigin.Begin) antes de escrever alguma coisa?

Cumprimentos,
Oliver Hanappi

Pelo que vale a pena, também encontrei o mesmo problema listado aqui. Para mim, o problema estava realmente com o código de upload, não o código de download:

    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

Neste exemplo, o problema é que eu declaro a matriz de bytes arrBuffer com um byte tamanho um muito grande. Este byte nulo é então salvo com a imagem do arquivo no banco de dados e reproduzido no download. O código corrigido seria:

        Dim arrBuffer(FileStream.Length - 1) As Byte

Também para referência meu HttpResponse O código é o seguinte:

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

Se você usar a abordagem acima, que usa a resposta.close (), o download de gerentes como o IE10 dirá 'não pode baixar o arquivo' porque os comprimentos dos bytes não correspondem aos cabeçalhos. Veja a documentação. Não use a resposta.close. SEMPRE. No entanto, o uso do verbo competerequest sozinho não desligará a redação de bytes para o fluxo de OPUT, para que aplicativos baseados em XML, como o Word 2007, verão o DOCX como corrompido. Nesse caso, quebre a regra para nunca usar a resposta. O código a seguir resolve os dois problemas. Seus resultados podem variar.

        '*** 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: você está usando o resposta.close-> Você pode tentar com o IE 10? Aposto que não funciona (as contagens de bytes não correspondem)

Tudo parece ok. Minha única idéia é tentar ligar para descartar seu fluxo depois de ligar para a resposta.flush em vez de antes, apenas para o caso de os bytes não esterem totalmente escritos antes de nivelar.

Dê uma olhada a isto: Escrevendo MemoryStream to Response Object

Eu tive o mesmo problema e a única solução que funcionou para mim foi:

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

Eu tive o mesmo problema enquanto tento abrir documentos .docx e .xlsx. Eu resolvo o problema definindo a configuração do servidor e o privato em vez de nocache

Há o meu método para chamar o documento:

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();          
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top