2009-08-19 11 views
6

W jednej z moich aplikacji internetowych asp.net muszę ukryć lokalizację pliku PDF dostarczanego użytkownikom.Próba przesłania pliku PDF za pomocą programu asp.net powoduje powstanie "uszkodzonego pliku".

Tak więc piszę metodę, która pobiera swoją zawartość binarną z jej lokalizacji w systemie CMS, a następnie opróżnia tablicę bajtów do użytkownika sieci.

Otrzymuję, niestety, błąd podczas pobierania strumienia: "Nie można otworzyć pliku, ponieważ jest uszkodzony" (lub coś podobnego do niego, podczas otwierania pliku w czytniku Adobe).

Pytanie 1: co robię źle? Pytanie 2: czy mogę pobierać duże pliki przy użyciu tego podejścia?

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

Dlaczego nie użyć metody Response.WriteFile? –

+0

Plik tak naprawdę nie istnieje w systemie plików. – Pablo

+0

Aby było bardziej zrozumiałe: plik istnieje w bazie danych CMS. Adres URL jest adresowalny, ale nie mogę pobrać pliku z systemu plików. – Pablo

Odpowiedz

7

Oto metoda używam. Przechodzi to z powrotem przywiązanie, więc IE tworzy okno dialogowe Otwórz/Zapisz.Zdaję się również wiedzieć, że pliki nie będą większe niż 1M, więc jestem pewien, że jest to czystszy sposób na wykonanie tego:

Miałem podobny problem z plikami PDF i Zdałem sobie sprawę, że absolutnie musiałem użyć strumieni binarnych i ReadBytes. Wszystko, co zawierały napisy, pomieszało to z.

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

Nie wiem, który fragment kodu zadziałał, ale w końcu działa :) Dzięki! – Pablo

+0

Czy używasz tego z ReportViewer? – JoshYates1980

+0

Wadą określania pewnej ilości bajtów jest to, że wszystkie te bajty zostaną zapisane w strumieniu, nawet jeśli plik nie jest tak duży. Możesz po prostu uzyskać długość strumienia i używać tego numeru zamiast wywoływać read.ReadBytes –

0

Mam coś podobnego działa na obecnej stronie internetowej 2.0. I pamiętam, jak walczyłem o to, aby to zadziałało, chociaż minęło trochę czasu, więc nie pamiętam zmagań.

Istnieje jednak kilka różnic między tym, co mam, a tym co masz. Mam nadzieję, że pomogą ci rozwiązać problem.

  • Po wywołaniu ClearContent wywoływam ClearHeaders();
  • Nie określam długości.
  • Nie mówię o składaniu
  • Wiem, że wszyscy mówią, aby tego nie robić, ale mam Response.Flush(); następnie Response.Close();

I jeszcze jedno, sprawdź swoją wartość contentType aby upewnić się, że jest prawidłowy dla plików PDF ((„application/pdf”).

1

ten fragment zrobił to dla mnie:

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

innych odpowiedzi skopiować zawartość pliku do pamięci przed wysłaniem odpowiedzi. Jeśli dane są już w pamięci, będziesz mieć dwie kopie, które nie są zbyt dobre dla skalowalności. To może jednak lepiej działać:

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

Ponieważ strumień jest zbiorem bajtów, nie ma potrzeby używania programu BinaryReader. Dopóki strumień wejściowy kończy się na końcu pliku, możesz po prostu użyć metody CopyTo() w strumieniu, który chcesz wysłać do przeglądarki internetowej. Cała zawartość zostanie zapisana w strumieniu docelowym bez wykonywania pośrednich kopii danych.

Jeśli trzeba tylko skopiować pewną liczbę bajtów ze strumienia, można utworzyć metodę rozszerzenia, który dodaje jeszcze kilka CopyTo() przeciąża:

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; 
     } 
    } 
} 

Można następnie używać go tak:

inputStream.CopyTo(response.OutputStream, contentLength); 

to będzie działać z każdym strumieniu wejściowym, ale szybkie przykładem może być czytanie pliku z systemu plików:

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

Jak wspomniano wcześniej, upewnij się, że typ MIME jest poprawny dla zawartości.

0

Kiedy używasz swojego kodu, kopiujesz bajty z pliku pdf prosto w swojej odpowiedzi. wszystkie kody ascii specjalnych muszą być zakodowane, aby przekazać w http. Podczas korzystania z funkcji stream strumień jest kodowany, więc nie musisz się tym martwić.

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

Ten fragment zawiera kod do czytania w pliku ze ścieżki pliku i wydobywania się nazwa pliku:

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

I czyta cały plik w tablicy bajtów i pozostawia plik otwarty. – CodeCaster

+0

Jaką zmianę byś polecił? Pozdrowienia –

+0

Czy muszę również wywołać Response.OutputStream.Close()? –

Powiązane problemy