Por qué lo son.archivos docx que se corrompen al descargar desde un ASP.NET ¿page?


Tengo este siguiente código para traer adjuntos de página al usuario:

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

El problema es que todos los archivos soportados funcionan correctamente (jpg, gif, png, pdf, doc, etc), pero .los archivos docx, cuando se descargan, están dañados y deben ser corregidos por Office para poder ser abiertos.

Al principio no sabía si el problema era descomprimir el archivo zip que contenía el .docx, así que en lugar de poner el archivo de salida solo en la respuesta, lo guardé primero, y el archivo se abrió con éxito, así que sé que el problema debe ser en la escritura de respuesta.

¿Sabes lo que puede estar sucediendo?

Author: Victor Rodrigues, 2010-03-19

8 answers

También me encontré con este problema y en realidad encontré la respuesta aquí: http://www.aspmessageboard.com/showthread.php?t=230778

Resulta que el formato docx necesita tener Respuesta.End () justo después de la respuesta.Escritura binaria.

 31
Author: Geoff Tanaka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-17 20:12:48

Al almacenar un archivo binario en SQL Server, tenga en cuenta que un archivo se rellena con la palabra boundry más cercana, por lo que potencialmente puede tener un byte adicional agregado a un archivo. La solución es almacenar el tamaño del archivo original en la base de datos cuando se almacena el archivo, y usarlo para la longitud que debe pasarse a la función de escritura del objeto Stream. "Flujo.Write (bytes (), 0, length)". Esta es la ÚNICA forma confiable de obtener el tamaño de archivo correcto, lo cual es muy importante para Office 2007 y arriba archivos, que no permiten caracteres adicionales al final de ellos (a la mayoría de los otros tipos de archivos como jpg no les importa).

 4
Author: Randall Spychalla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-19 13:22:04

No debe usar stream.GetBuffer() porque devuelve la matriz de búfer que podría contener bytes no utilizados. Use stream.ToArray() en su lugar. Además, ¿has intentado llamar stream.Seek(0, SeekOrigin.Begin) antes de escribir algo?

Saludos cordiales,
Oliver Hanappi

 2
Author: Oliver Hanappi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-19 13:46:04

Por si sirve de algo, también me encontré con el mismo problema enumerado aquí. Para mí el problema era en realidad con el código de carga no el código de descarga:

    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

En este ejemplo el problema es que declaro la matriz de bytes arrBuffer con un tamaño de un byte demasiado grande. Este byte nulo se guarda con la imagen del archivo en la base de datos y se reproduce durante la descarga. El código corregido sería:

        Dim arrBuffer(FileStream.Length - 1) As Byte

También para referencia mi código HttpResponse es el siguiente:

                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
Author: pseudocoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-25 23:13:14

Si utiliza el enfoque anterior que utiliza la respuesta.Close(), los gestores de descargas como IE10 dirán 'no se puede descargar el archivo' porque las longitudes de bytes no coinciden con las cabeceras. Consulte la documentación. NO utilice respuesta.Cerca. NUNCA. Sin embargo, el uso del verbo CompeteRequest por sí solo no apaga la escritura de bytes en la secuencia de salida, por lo que las aplicaciones basadas en XML como WORD 2007 verán el docx como dañado. En este caso, rompa la regla para NUNCA usar Respuesta.Final. El siguiente código resuelve ambos problemas. Sus resultados pueden 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: estás usando la respuesta.Cerrar Close > ¿puedes probarlo con IE 10? bet it doesn't work (byte counts don't match)

 1
Author: PhilM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-16 01:05:43

Todo se ve bien. Mi única idea es intentar llamar a Dispose en tu transmisión después de llamar a Response.Enjuague en lugar de antes, solo en caso de que los bytes no estén completamente escritos antes de enjuagar.

 0
Author: Ray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-19 13:40:42

Echa un vistazo a esto: Escribiendo MemoryStream al Objeto de respuesta

Tuve el mismo problema y la única solución que funcionó para mí fue:

    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
Author: playful,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:00

Tuve el mismo problema mientras intentaba abrir .docx y .documentos xlsx. Resuelvo el problema definiendo la cacheabilidad a ServerAndPrivate en lugar de NoCache

Existe mi método para llamar a document:

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
Author: Stephane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-09 14:13:55