2010-03-19 23 views
24

Tôi có này đoạn mã sau để đưa file đính kèm trang cho người sử dụng:Tại sao các tệp .docx bị hỏng khi tải xuống từ trang 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(); 
} 

Vấn đề là tất cả các file được hỗ trợ hoạt động đúng (jpg, gif, png, pdf, doc, vv), nhưng file .docx, khi tải xuống, bị hỏng và cần phải được Office cố định để mở. Lúc đầu, tôi không biết liệu sự cố có đang giải nén tệp nén chứa .docx hay không, vì vậy thay vì chỉ đặt tệp đầu ra vào phản hồi, trước tiên tôi đã lưu tệp đó và tệp đã mở thành công, vì vậy Tôi biết vấn đề nên được viết phản hồi.

Bạn có biết điều gì có thể xảy ra không?

+2

này vấp tôi dậy khi xuất ra PDF. Hóa ra người xem PDF sẽ dung thứ rác bất ngờ sau khi kết thúc dữ liệu hợp lệ và tôi đã thêm HTML được hiển thị của trang vào mọi tệp PDF tôi đã gửi. Có thể giống với các định dạng tệp nhị phân khác, chúng không quan tâm đến dữ liệu không mong muốn được nối vào dữ liệu hợp lệ. – millimoose

Trả lời

29

Tôi cũng chạy vào vấn đề này và thực sự tìm thấy câu trả lời ở đây: http://www.aspmessageboard.com/showthread.php?t=230778

Nó chỉ ra rằng định dạng docx cần có Response.End() ngay sau khi Response.BinaryWrite.

+0

Chỉ cần tiết kiệm cho tôi số giờ rà soát lưới tìm kiếm điều này, cảm ơn !! Nó có ý nghĩa quá bởi vì các tập tin có một số bit khác nối vào cuối của nó từ dòng và nó kết thúc lên lớn hơn một chút so với trên máy chủ. –

+0

Thêm đáp ứng.End() VÀ giới hạn đầu ra cho các tệp gốc (như giải pháp của Randall Spychalla bên dưới) đều được yêu cầu để tôi giải quyết yêu cầu này –

+0

.Đúng không bắt buộc nhưng việc đặt độ dài tệp là như vậy khi nó biết xong rôi. – Shawn

0

Tất cả đều ổn. Ý tưởng duy nhất của tôi là thử gọi Dispose trên luồng của bạn sau khi gọi Response.Flush thay vì trước đó, chỉ trong trường hợp các byte không được viết hoàn toàn trước khi xả.

+0

Tôi cũng làm điều này, không thành công. –

+1

chỉ cần tham gia một giả đoán hoang dã ở đây ... hãy thử sử dụng loại nội dung "application/octet-stream" và xem bạn có tải xuống tệp hợp lệ hay không. btw - điều này có thể phù hợp hơn "ứng dụng/không xác định" khi bạn không biết loại tệp. – Ray

2

Bạn không nên sử dụng stream.GetBuffer() vì nó trả về mảng đệm có thể chứa các byte không sử dụng. Sử dụng stream.ToArray() để thay thế. Ngoài ra, bạn đã thử gọi stream.Seek(0, SeekOrigin.Begin) trước khi viết bất cứ điều gì?

Best Regards,
Oliver Hanappi

+0

Tôi đã làm những việc này, nhưng cũng không giải quyết được vấn đề =/ –

4

Khi lưu trữ tệp nhị phân trong SQL Server, hãy lưu ý rằng tệp được đệm vào giới hạn từ gần nhất, do đó bạn có thể có thêm byte được thêm vào tệp. Giải pháp là lưu trữ kích thước tệp ban đầu trong db khi bạn lưu trữ tệp và sử dụng nó cho độ dài cần được chuyển đến hàm ghi của đối tượng Luồng. "Stream.Write (byte(), 0, length)". Đây là cách đáng tin cậy duy nhất để nhận được kích thước tệp chính xác, điều này rất quan trọng đối với Office 2007 và các tệp, không cho phép các ký tự thừa ở cuối chúng (hầu hết các loại tệp khác như jpg không quan tâm).

0

Hãy xem một này: Writing MemoryStream to Response Object

tôi đã cùng một vấn đề và giải pháp duy nhất làm việc cho tôi là:

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

Nếu bạn sử dụng phương pháp trên trong đó sử dụng response.Close() , Trình quản lý tải xuống như IE10 sẽ cho biết 'không thể tải xuống tệp' vì độ dài byte không khớp với tiêu đề. Xem tài liệu. KHÔNG sử dụng response.Close. KHÔNG BAO GIỜ. Tuy nhiên, việc sử dụng động từ CompeteRequest một mình không tắt việc ghi byte vào luồng ouput để các ứng dụng dựa trên XML như WORD 2007 sẽ thấy docx bị hỏng. Trong trường hợp này, hãy phá vỡ quy tắc để KHÔNG BAO GIỜ sử dụng Response.End. Mã sau đây giải quyết cả hai vấn đề. Kết quả của bạn có thể thay đổi.

 '*** 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: bạn đang sử dụng response.Close -> bạn có thể dùng thử với IE 10 không? đặt cược nó không hoạt động (số byte không khớp)

0

Tôi đã gặp vấn đề tương tự trong khi tôi cố gắng mở tài liệu .docx và .xlsx.Tôi giải quyết vấn đề bằng cách xác định năng bộ nhớ cache để ServerAndPrivate thay vì nocache

có phương pháp của tôi để gọi tài liệu:

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

Đối với những gì nó có giá trị, tôi cũng chạy vào cùng một vấn đề được liệt kê ở đây. Đối với tôi vấn đề này đã thực sự với mã tải lên không mã 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 

Trong ví dụ này, vấn đề là tôi khai báo mảng Byte arrBuffer với kích thước một byte quá lớn. Byte rỗng này sau đó được lưu với hình ảnh tệp vào DB và được sao chép khi tải xuống. Mã chỉnh sẽ là:

 Dim arrBuffer(FileStream.Length - 1) As Byte 

Ngoài ra để tham khảo đang HttpResponse của tôi là như sau:

   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() 
Các vấn đề liên quan