2010-03-19 9 views
24

ho questa seguente codice per portare gli allegati di pagina per l'utente:Perché i file .docx sono danneggiati durante il download da una pagina 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(); 
} 

Il problema è che tutti i file supportati funziona correttamente (jpg, gif, png, pdf, doc, ecc), ma i file .docx, quando vengono scaricati, sono corrotti e devono essere riparati da Office per poter essere aperti.

All'inizio non sapevo se il problema era nel decomprimere il file zip che conteneva il file .docx, quindi invece di mettere il file di output solo nella risposta, l'ho salvato prima, e il file è stato aperto correttamente, quindi So che il problema dovrebbe essere alla risposta.

Sai cosa può succedere?

+2

Questo mi ha scattato quando vengono emessi in formato PDF. Si scopre che i visualizzatori PDF tollereranno la spazzatura inaspettata dopo la fine dei dati validi, e aggiungevo HTML di rendering della pagina a ogni file PDF che stavo inviando. Potrebbe essere lo stesso per altri formati di file binari, a loro non interessa dati inaspettati aggiunti a dati validi. – millimoose

risposta

29

Ho anche imbattuto in questo problema ed effettivamente trovato la risposta qui: http://www.aspmessageboard.com/showthread.php?t=230778

Si scopre che il formato docx deve avere Response.End() subito dopo il Response.BinaryWrite.

+0

Mi ha appena salvato ore di pesca a strascico alla ricerca di questo, grazie !! Ha senso anche perché il file sta aggiungendo altri bit alla fine dello stream e finisce per ingrandirsi leggermente rispetto al server. –

+0

Aggiunta di una Response.End() E limitando l'output al file originale (come soluzione di Randall Spychalla sotto) sono stati entrambi necessari per risolvere questa risposta –

+0

.End non è obbligatorio, ma l'impostazione della lunghezza del file è così da sapere quando E 'fatto. – Shawn

0

Tutto sembra ok. La mia unica idea è di provare a chiamare Dispose sul tuo stream dopo aver chiamato Response.Flush invece di prima, nel caso in cui i byte non siano interamente scritti prima dello svuotamento.

+0

Ho fatto anche questo, senza successo. –

+1

provando semplicemente a indovinare qui ... prova usando il tipo di contenuto "application/octet-stream" e vedi se ottieni un file valido scaricato. btw - questo è probabilmente più appropriato di "application/unknown" quando non si conosce il tipo di file. – Ray

2

Non utilizzare stream.GetBuffer() perché restituisce l'array di buffer che potrebbe contenere byte inutilizzati. Utilizzare invece stream.ToArray(). Inoltre, hai provato a chiamare stream.Seek(0, SeekOrigin.Begin) prima di scrivere qualcosa?

migliori saluti,
Oliver Hanappi

+0

Ho fatto queste cose, ma nessuno dei due ha risolto il problema =/ –

4

Quando si memorizza un file binario in SQL Server, tenere presente che un file viene riempito con il file di parole più vicino, quindi è possibile aggiungere un byte aggiuntivo a un file. La soluzione consiste nel memorizzare la dimensione del file originale nel db quando si archivia il file e utilizzarlo per la lunghezza che deve essere passata alla funzione di scrittura dell'oggetto Stream. "Stream.Write (byte(), 0, lunghezza)". Questo è l'UNICO modo affidabile per ottenere la dimensione corretta del file, che è molto importante per Office 2007 e file, che non consentono la presenza di caratteri extra alla fine (la maggior parte degli altri tipi di file come jpg non interessa).

0

Date un'occhiata a questo: Writing MemoryStream to Response Object

Ho avuto lo stesso problema e l'unica soluzione che ha funzionato per me era:

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

Se si utilizza l'approccio di cui sopra che utilizza response.Close() , Download manager come IE10 diranno 'impossibile scaricare il file' perché le lunghezze dei byte non corrispondono alle intestazioni. Vedi la documentazione. NON usare response.Close. MAI. Tuttavia, l'uso del verbo CompeteRequest non interrompe la scrittura dei byte nel flusso di output in modo che le applicazioni basate su XML come WORD 2007 vedano il documento come danneggiato. In questo caso, interrompere la regola per MAI utilizzare Response.End. Il seguente codice risolve entrambi i problemi. I risultati possono variare.

 '*** 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: si sta utilizzando response.Close -> si può provare con IE 10? scommetto che non funziona (i conteggi dei byte non corrispondono)

0

Ho avuto lo stesso problema mentre provo ad aprire i documenti .docx e .xlsx.Ho risolto il problema definendo la cacheability a ServerAndPrivate invece di NoCache

c'è il mio metodo da chiamare 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

Per quello che vale, ho anche incontrato lo stesso problema elencato qui. Per me la questione era in realtà con il codice di caricamento non il codice per il 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 

In questo esempio il problema è dichiaro la matrice di byte arrBuffer con una dimensione di un byte troppo grandi. Questo byte null viene quindi salvato con l'immagine del file nel DB e riprodotto al download. Il codice corretto sarebbe:

 Dim arrBuffer(FileStream.Length - 1) As Byte 

anche per riferimento il mio codice HttpResponse è il seguente:

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