¿Por qué son los archivos .docx siendo corrompidos cuando se descarga desde una página ASP.NET?
-
21-09-2019 - |
Pregunta
Tengo este código siguiente para traer archivos adjuntos de página para el 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 funciona correctamente (JPG, GIF, PNG, PDF, DOC, etc), pero los archivos .docx, cuando descargado, están dañados y necesitan ser fijado por la Oficina con el fin de ser abierto.
Al principio no sabía si el problema estaba en descomprimir el archivo zip que contiene el .docx, así que en vez de poner el archivo de salida sólo en la respuesta, lo guardé en primer lugar, y el archivo abierto con éxito, por lo que sabe que el problema debe estar en la escritura de respuesta.
¿Sabe lo que puede estar pasando?
Solución
También 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 () inmediatamente después de la Response.BinaryWrite.
Otros consejos
Al almacenar un archivo binario en SQL Server, tenga en cuenta que un archivo se rellena a la palabra sitio limítrofe más cercano, por lo que puede llegar a tener un byte adicional agregado a un archivo. La solución es almacenar el tamaño del archivo original en el PP cuando vaya a guardar el archivo, y el uso que de la longitud que necesita ser pasado a la función de escritura del objeto Stream. "Stream.Write (bytes (), 0, longitud)". Esta es la ÚNICA manera fiable de conseguir el tamaño correcto de archivos, lo cual es muy importante para Office 2007 y de seguridad de archivos, que no permita que los caracteres adicionales a estar en el extremo de ellos (la mayoría de los otros tipos de archivo como jpg de no les importa).
No use stream.GetBuffer()
porque devuelve la matriz de memoria intermedia que podría contener bytes no utilizados. Utilice stream.ToArray()
lugar. Además, ¿ha intentado llamar stream.Seek(0, SeekOrigin.Begin)
antes de escribir algo?
Mejores Saludos, Oliver Hanappi
Por lo que vale la pena, también me encontré con el mismo problema que aparece aquí. Para mí, la cuestión era en realidad no con el código carga 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 arrBuffer
matriz de bytes con un tamaño de un byte demasiado grande. Este byte nulo se guarda la imagen del archivo de la base de datos con y reproducido de 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()
Si se utiliza el método anterior que utiliza response.Close (), Gestores de descargas tales como IE10 dirán 'no puede descargar el fichero' debido a que las longitudes de bytes no coinciden con las cabeceras. Consulte la documentación. NO utilice response.Close. SIEMPRE. Sin embargo, utilizando el verbo CompeteRequest solos no se apaga la escritura de bytes a la corriente ouput lo que las aplicaciones basadas en XML como por ejemplo Word 2007 verán la docx como está dañado. En este caso, romper la regla de no usar NUNCA Response.End. 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á utilizando response.Close -> se puede probar con IE 10? apuesto a que no funciona (cantidad de bytes no coinciden)
Todo se ve bien. Mi única idea es tratar de llamar a Dispose en su torrente después de llamar Response.Flush en vez de antes, por si acaso los bytes no están escritos en su totalidad antes de enjuagar.
Tome un aspecto un siguiente: escritura MemoryStream al objeto respuesta
Yo 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();
Yo tenía el mismo problema, mientras que intento abrir .docx y .xlsx documentos. 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();
}