2009-08-19 42 views
6

Trong một trong các ứng dụng web asp.net của tôi, tôi cần phải ẩn vị trí của tệp pdf đang được cung cấp cho người dùng.Đang cố gắng phát một tệp PDF với asp.net đang tạo một "tệp bị hỏng"

Vì vậy, tôi đang viết một phương thức truy xuất nội dung nhị phân từ vị trí của nó trên hệ thống CMS và sau đó xóa một mảng byte cho người dùng web.

Tôi nhận được, rất tiếc, đã xảy ra lỗi khi tải xuống luồng: "Không thể mở tệp vì nó bị hỏng" (hoặc điều gì đó tương tự như vậy, khi mở tệp trong trình đọc adobe).

Câu hỏi 1: Tôi đang làm gì sai? Câu hỏi 2: Tôi có thể tải xuống các tệp lớn bằng cách sử dụng phương pháp này không?

private void StreamFile(IItem documentItem) 
    { 
     //CMS vendor specific API 
     BinaryContent itemBinaryContent = documentItem.getBinaryContent(); 
     //Plain old .NET 
     Stream fileStream = itemBinaryContent.getContentStream(); 
     var len = itemBinaryContent.getContentLength(); 
     SendStream(fileStream, len, itemBinaryContent.getContentType()); 
    } 

    private void SendStream(Stream stream, int contentLen, string contentType) 
    { 
     Response.ClearContent(); 
     Response.ContentType = contentType; 
     Response.AppendHeader("content-Disposition", string.Format("inline;filename=file.pdf")); 
     Response.AppendHeader("content-length", contentLen.ToString()); 
     var bytes = new byte[contentLen]; 
     stream.Read(bytes, 0, contentLen); 
     stream.Close(); 
     Response.BinaryWrite(bytes); 
     Response.Flush(); 
    } 
+1

Tại sao không sử dụng phương pháp Response.WriteFile? –

+0

Tệp không thực sự tồn tại trong hệ thống tệp. – Pablo

+0

Để rõ ràng hơn: tệp tồn tại trong cơ sở dữ liệu CMS. Đó là địa chỉ url, nhưng tôi không thể truy xuất tệp từ hệ thống tệp. – Pablo

Trả lời

7

Dưới đây là một phương pháp tôi sử dụng. Điều này sẽ trả lại một tập tin đính kèm, vì vậy IE tạo ra một hộp thoại Mở/Lưu. Tôi cũng tình cờ biết rằng các tệp sẽ không lớn hơn 1 triệu, vì vậy tôi chắc chắn có một cách rõ ràng hơn để thực hiện việc này. Tôi nhận ra rằng tôi hoàn toàn phải sử dụng các luồng nhị phân và ReadBytes. Bất kỳ thứ gì có chuỗi đã bị làm rối tung lên.

Stream stream = GetStream(); // Assuming you have a method that does this. 
BinaryReader reader = new BinaryReader(stream); 

HttpResponse response = HttpContext.Current.Response; 
response.ContentType = "application/pdf"; 
response.AddHeader("Content-Disposition", "attachment; filename=file.pdf"); 
response.ClearContent(); 
response.OutputStream.Write(reader.ReadBytes(1000000), 0, 1000000); 

// End the response to prevent further work by the page processor. 
response.End(); 
+0

Bạn không biết mã nào làm cho mã hoạt động, nhưng cuối cùng cũng hoạt động :) Cảm ơn! – Pablo

+0

Bạn có sử dụng điều này với ReportViewer không? – JoshYates1980

+0

Nhược điểm của việc xác định một số byte nhất định là tất cả các byte đó sẽ được ghi vào luồng ngay cả khi tệp không lớn như vậy. Bạn chỉ có thể nhận được độ dài của luồng và sử dụng số đó thay vì khi gọi trình đọc.ReadBytes –

0

Tôi có thứ tương tự đang hoạt động trên trang web 2.0 hiện tại. Và tôi nhớ phải vật lộn để có được nó để làm việc mặc dù nó được một thời gian vì vậy tôi không nhớ những cuộc đấu tranh.

Có một số khác biệt giữa những gì tôi có những gì bạn có. Hy vọng rằng những điều này sẽ giúp bạn giải quyết vấn đề.

  • Sau cuộc gọi ClearContent, tôi gọi ClearHeaders();
  • Tôi không chỉ định độ dài.
  • Tôi không chỉ định nội dòng trên Disposition
  • Tôi biết mọi người nói không làm điều đó, nhưng tôi có một Response.Flush(); tiếp theo là Response.Close();

Và, có một điều khác, kiểm tra giá trị contentType của bạn để chắc chắn rằng nó là chính xác cho PDF (("Ứng dụng/pdf").

1

Snippet này đã làm nó cho tôi:

   Response.Clear(); 
       Response.ClearContent(); 
       Response.ClearHeaders(); 
       Response.ContentType = mimeType; 
       Response.AddHeader("Content-Disposition", "attachment"); 
       Response.WriteFile(filePath); 
       Response.Flush(); 
6

Những câu trả lời khác sao chép nội dung tập tin vào bộ nhớ trước khi gửi phản hồi. Nếu dữ liệu đã có trong bộ nhớ, thì bạn sẽ có hai bản sao, mà không phải là rất tốt cho khả năng mở rộng. Thay vào đó, điều này có thể hoạt động tốt hơn:

public void SendFile(Stream inputStream, long contentLength, string mimeType, string fileName) 
{ 
    string clength = contentLength.ToString(CultureInfo.InvariantCulture); 
    HttpResponse response = HttpContext.Current.Response; 
    response.ContentType = mimeType; 
    response.AddHeader("Content-Disposition", "attachment; filename=" + fileName); 
    if (contentLength != -1) response.AddHeader("Content-Length", clength); 
    response.ClearContent(); 
    inputStream.CopyTo(response.OutputStream); 
    response.OutputStream.Flush(); 
    response.End(); 
} 

Vì luồng là tập hợp các byte nên không cần sử dụng BinaryReader. Và miễn là luồng đầu vào kết thúc ở cuối tệp, thì bạn chỉ có thể sử dụng phương thức CopyTo() trên luồng bạn muốn gửi tới trình duyệt web. Tất cả nội dung sẽ được ghi vào luồng mục tiêu, mà không tạo bất kỳ bản sao trung gian nào của dữ liệu.

Nếu bạn cần phải chỉ sao chép một số lượng nhất định các byte từ luồng dữ liệu, bạn có thể tạo ra một phương pháp khuyến nông có thêm một vài chi tiết CopyTo() quá tải:

public static class Extensions 
{ 
    public static void CopyTo(this Stream inStream, Stream outStream, long length) 
    { 
     CopyTo(inStream, outStream, length, 4096); 
    } 

    public static void CopyTo(this Stream inStream, Stream outStream, long length, int blockSize) 
    { 
     byte[] buffer = new byte[blockSize]; 
     long currentPosition = 0; 

     while (true) 
     { 
      int read = inStream.Read(buffer, 0, blockSize); 
      if (read == 0) break; 
      long cPosition = currentPosition + read; 
      if (cPosition > length) read = read - Convert.ToInt32(cPosition - length); 
      outStream.Write(buffer, 0, read); 
      currentPosition += read; 
      if (currentPosition >= length) break; 
     } 
    } 
} 

Sau đó, bạn có thể sử dụng nó như vậy:

inputStream.CopyTo(response.OutputStream, contentLength); 

này sẽ làm việc với bất kỳ dòng đầu vào, nhưng một ví dụ nhanh sẽ đọc một tập tin từ hệ thống tập tin:

string filename = @"C:\dirs.txt"; 
using (FileStream fs = File.Open(filename, FileMode.Open)) 
{ 
    SendFile(fs, fs.Length, "application/octet-stream", filename); 
} 

Như đã đề cập trước đây, hãy đảm bảo loại MIME của bạn là chính xác cho nội dung.

0

khi bạn sử dụng mã của mình, bạn đang sao chép byte từ pdf ngay trong phản hồi của mình. tất cả các mã ascii đặc biệt cần phải được mã hóa để chuyển vào http. Khi sử dụng chức năng dòng, luồng được mã hóa để bạn không phải lo lắng về nó.

var bytes = new byte[contentLen]; 
    stream.Read(bytes, 0, contentLen); 
    stream.Close(); 
    Response.BinaryWrite(bytes); 
0

đoạn này bao gồm các mã cho đọc trong file từ một đường dẫn tập tin, và giải nén ra tên tập tin:

private void DownloadFile(string path) 
{ 
    using (var file = System.IO.File.Open(path, FileMode.Open)) 
    { 
     var buffer = new byte[file.Length]; 
     file.Read(buffer, 0, (int)file.Length); 

     var fileName = GetFileName(path); 
     WriteFileToOutputStream(fileName, buffer); 
    } 
} 

private string GetFileName(string path) 
{ 
    var fileNameSplit = path.Split('\\'); 
    var fileNameSplitLength = fileNameSplit.Length; 
    var fileName = fileNameSplit[fileNameSplitLength - 1].Split('.')[0]; 
    return fileName; 
} 

private void WriteFileToOutputStream(string fileName, byte[] buffer) 
{ 
    Response.Clear(); 
    Response.ContentType = "application/pdf"; 
    Response.AddHeader("Content-Disposition", 
     $"attachment; filename\"{fileName}.pdf"); 
    Response.OutputStream.Write(buffer, 0, buffer.Length); 
    Response.OutputStream.Close(); 
    Response.Flush(); 
    Response.End(); 
} 
+0

Và nó đọc toàn bộ tệp trong một mảng byte và để tệp mở. – CodeCaster

+0

Bạn sẽ đề xuất thay đổi nào? Chúc mừng –

+0

Tôi có cần gọi Response.OutputStream.Close() không? –

Các vấn đề liên quan