2010-03-19 13 views
24

Mam to poniższy kod na wniesienie załączników strony do użytkownika:Dlaczego pliki .docx są uszkodzone podczas pobierania ze strony 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(); 
} 

Problemem jest to, że wszystkie obsługiwane pliki działa poprawnie (JPG, GIF, PNG, PDF, doc itp), ale pliki .docx, po pobraniu są uszkodzone i muszą zostać naprawione przez Office w celu otwarcia.

Na początku nie wiedziałem, czy problem polegał na dekompresji pliku zip zawierającego plik .docx, więc zamiast zapisywać plik wyjściowy tylko w odpowiedzi, zapisałem go najpierw, a plik otworzył się pomyślnie, więc Wiem, że problem powinien dotyczyć pisania odpowiedzi.

Czy wiesz, co może się dziać?

+2

ten potknął się, gdy mi wyprowadzania PDF. Okazuje się, że przeglądarki plików PDF będą tolerować nieoczekiwane śmieci po zakończeniu poprawnych danych, a ja dodałem wyrenderowany kod HTML strony do każdego wysyłanego pliku PDF. Może być tak samo w przypadku innych formatów plików binarnych, nie interesują ich nieoczekiwane dane dołączone do ważnych danych. – millimoose

Odpowiedz

29

ja też wpadłem na ten problem i faktycznie okazało się, że odpowiedź tutaj: http://www.aspmessageboard.com/showthread.php?t=230778

Okazuje się, że format docx musi mieć Response.End() zaraz po Response.BinaryWrite.

+0

Właśnie uratowałem mi godziny trałowania sieci w poszukiwaniu tego, dzięki! Ma to sens także dlatego, że plik zawiera kilka innych bitów dołączonych do jego końca ze strumienia i kończy się nieco większy niż na serwerze. –

+0

Dodanie Response.End() I ograniczenie wyjścia do oryginalnego rozmiaru pliku (jak rozwiązanie Randall Spychalla poniżej) było wymagane do rozwiązania tego problemu –

+0

Response.End nie jest obowiązkowe, ale ustawienie długości pliku jest takie, aby wiedzieć, kiedy zrobione. – Shawn

0

Wszystko wygląda dobrze. Moim jedynym pomysłem jest próba wywołania Dispose w twoim strumieniu po wywołaniu Response.Flush zamiast wcześniej, na wypadek, gdyby bajty nie zostały w całości napisane przed opróżnieniem.

+0

Zrobiłem to również, bez powodzenia. –

+1

po prostu zgadując, jak to zrobić ... spróbuj użyć typu zawartości "application/octet-stream" i sprawdź, czy otrzymałeś prawidłowy plik. btw - jest to prawdopodobnie bardziej odpowiednie niż "application/unknown", gdy nie znasz typu pliku. – Ray

2

Nie powinieneś używać stream.GetBuffer(), ponieważ zwraca tablicę buforów, która może zawierać nieużywane bajty. Zamiast tego użyj stream.ToArray(). Próbowałeś też dzwonić pod numer stream.Seek(0, SeekOrigin.Begin) przed napisaniem czegokolwiek?

Pozdrawiam,
Oliver Hanappi

+0

Zrobiłem te rzeczy, ale też nie rozwiązałem problemu =/ –

4

Podczas przechowywania pliku binarnego w programie SQL Server należy pamiętać, że plik jest dopełniany do najbliższej granicy wyrazów, więc potencjalnie można dodać do pliku dodatkowy bajt. Rozwiązaniem jest przechowywanie oryginalnego rozmiaru pliku w db podczas zapisywania pliku i używanie go dla długości, która musi zostać przekazana do funkcji zapisu obiektu Stream. "Stream.Write (bytes(), 0, długość)". Jest to jedyny niezawodny sposób na uzyskanie prawidłowego rozmiaru pliku, co jest bardzo ważne dla pakietu Office 2007 i plików w górę, które nie pozwalają na umieszczenie dodatkowych znaków (większość innych typów plików, takich jak jpg, nie obchodzi ich).

0

Weź wyglądać następująco: Writing MemoryStream to Response Object

miałem ten sam problem, a jedynym rozwiązaniem, który pracował dla mnie było:

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

Jeśli używasz podejście powyżej którego używa response.Close() , Menedżerowie pobierania, tacy jak IE10, powiedzą "nie można pobrać pliku", ponieważ długości bajtów nie pasują do nagłówków. Zobacz dokumentację. NIE używaj odpowiedzi.Close. ZAWSZE. Jednak używanie samego czasownika CompeteRequest nie wyłącza zapisywania bajtów do strumienia ouput, więc aplikacje oparte na XML, takie jak WORD 2007, zobaczą plik docx jako uszkodzony. W takim przypadku należy złamać regułę, aby NIGDY nie używać opcji Response.End. Poniższy kod rozwiązuje oba problemy. Twoje wyniki mogą się różnić.

 '*** 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. 

@ Cezar: używasz odpowiedzi. Zamknij -> możesz wypróbować za pomocą IE 10? zakład nie działa (liczba bajtów nie pasuje)

0

Miałem ten sam problem, gdy próbuję otwierać dokumenty .docx i .xlsx.I rozwiązać problem poprzez zdefiniowanie cacheability do ServerAndPrivate zamiast nocache

jest moja metoda zadzwonić dokument:

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

Na co warto, ja też wpadłem na ten sam problem tutaj wymienione. Dla mnie sprawa była rzeczywiście nie z kodem odczytanie kodu do pobrania:

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 

W tym przykładzie problem jest zadeklarować tablicę bajtów arrBuffer o wielkości jednego bajtu zbyt duże. Ten bajt zerowy jest następnie zapisywany wraz z obrazem pliku do bazy danych i odtwarzany podczas pobierania. Skorygowana kod byłoby:

 Dim arrBuffer(FileStream.Length - 1) As Byte 

także odniesienie mój kod HttpResponse się następująco:

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