2010-03-19 17 views
24

tengo el código siguiente para traer archivos adjuntos de página para el usuario:¿Por qué los archivos .docx están dañados al descargar desde una página 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(); 
} 

El problema es que todos los archivos soportados funciona correctamente (JPG, GIF, PNG, PDF, DOC, etc), pero los archivos .docx, cuando se descargan, están corruptos y necesitan ser arreglados por Office para poder abrirlos.

Al principio no sabía si el problema era descomprimir el archivo comprimido 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ó correctamente, por lo que Sé que el problema debería estar en la redacción de respuestas.

¿Sabes lo que puede estar pasando?

+2

Esto me tropezó cuando se emiten PDF. Resulta que los lectores de PDF tolerarán la basura inesperada después del final de los datos válidos, y yo estaba agregando el HTML renderizado de la página a cada archivo PDF que estaba enviando. Puede ser el mismo para otros formatos de archivo binarios, no les importan los datos inesperados agregados a datos válidos. – millimoose

Respuesta

29

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

Resulta que el formato docx necesita tener Response.End() justo después de el Response.BinaryWrite.

+0

¡Me salvó horas de búsqueda de redes, gracias! Tiene sentido también porque el archivo tiene algunos otros bits añadidos al final de la secuencia y termina un poco más grande que en el servidor. –

+0

Agregando un Response.End() Y limitando el resultado al tamaño del archivo original (como la solución de Randall Spychalla a continuación) fueron necesarios para resolver este –

+0

Response.End no es obligatorio, pero establecer la longitud del archivo es para que sepa cuándo está hecho. – Shawn

0

Todo se ve bien. Mi única idea es intentar llamar a Dispose en la secuencia después de llamar a Response.Flush en lugar de antes, solo en caso de que los bytes no se escriban completamente antes de la descarga.

+0

Lo hice también, sin éxito. –

+1

simplemente adivinando aquí ... pruébalo usando el tipo de contenido "application/octet-stream" y verifica si descargaste un archivo válido. Por cierto, esto es probablemente más apropiado que "aplicación/desconocido" cuando no se conoce el tipo de archivo. – Ray

2

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, ¿ha intentado llamar al stream.Seek(0, SeekOrigin.Begin) antes de escribir algo?

Best Regards,
Oliver Hanappi

+0

Hice estas cosas, pero tampoco resolvió el problema =/ –

4

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

0

Tome un vistazo a esto: Writing MemoryStream to Response Object

que tenía 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(); 
1

Si se utiliza el método anterior que utiliza response.Close() Los administradores de descargas como IE10 dirán "no se puede descargar el archivo" porque la longitud de los bytes no coincide con los encabezados. Ver la documentación. NO use la respuesta. Cerrar. NUNCA. Sin embargo, usar el verbo CompeteRequest por sí solo no bloquea la escritura de bytes en la secuencia de salida, por lo que las aplicaciones basadas en XML como WORD 2007 verán el documento como corrupto. En este caso, rompa la regla para NUNCA usar Response.End. El siguiente código resuelve ambos problemas. Tus 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 utilizando la respuesta.Cerrar -> ¿puedes probarlo con IE 10? apuesta que no funciona (el número de bytes no coincide)

0

Tuve el mismo problema al intentar abrir documentos .docx y .xlsx.Resuelvo el problema mediante la definición de los caches a ServerAndPrivate en lugar de NoCache

no es mi método para llamar 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();   
    } 
2

Por lo que vale la pena, que también tuvo el mismo problema que aparece aquí. Para mí, la cuestión era en realidad con el código de carga no es 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 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 el DB y se reproduce en la descarga. El código corregido sería:

 Dim arrBuffer(FileStream.Length - 1) As Byte 

También para mi código de referencia 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() 
Cuestiones relacionadas