2009-08-19 11 views
6

En una de mis aplicaciones web asp.net necesito ocultar la ubicación de un archivo pdf que se está sirviendo a los usuarios.Intentar transmitir un archivo PDF con asp.net está produciendo un "archivo dañado"

Por lo tanto, estoy escribiendo un método que recupera su contenido binario de su ubicación en un sistema CMS y luego vacía una matriz de bytes para el usuario web.

Obtengo, desafortunadamente, un error al descargar la secuencia: "No se pudo abrir el archivo porque está dañado" (o algo similar a eso, al abrir el archivo en Adobe Reader).

Pregunta 1: ¿Qué estoy haciendo mal? Pregunta 2: ¿puedo descargar archivos de gran tamaño usando este enfoque?

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

Por qué no usar el método Response.WriteFile? –

+0

El archivo realmente no existe en el sistema de archivos. – Pablo

+0

Para ser más claros: el archivo existe en la base de datos CMS. Es URL direccionable, pero realmente no puedo recuperar el archivo del sistema de archivos. – Pablo

Respuesta

7

Aquí es un método que utilizo. Esto pasa de nuevo un archivo adjunto, IE produce un diálogo Abrir/Guardar. También sé que los archivos no serán más grandes que 1M, así que estoy seguro de que hay una manera más clara de hacerlo.

Tuve un problema similar con los PDF, y Me di cuenta de que tenía que usar flujos binarios y ReadBytes. Cualquier cosa con cadenas lo estropeó.

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

No sé qué bit de código lo hizo funcionar, pero finalmente está funcionando :) ¡Gracias! – Pablo

+0

¿Lo usa con ReportViewer? – JoshYates1980

+0

La desventaja de especificar una cierta cantidad de bytes es que todos esos bytes se escribirán en la secuencia, incluso si el archivo no es tan grande como eso. Puede simplemente obtener la longitud de la secuencia y usar ese número en su lugar cuando llame al lector.ReadBytes –

0

Tengo algo similar trabajando en un sitio web actual 2.0. Y recuerdo haber luchado para que funcione, aunque ha pasado un tiempo, así que no recuerdo las dificultades.

Sin embargo, existen algunas diferencias entre lo que tengo y lo que tiene. Espero que esto te ayude a resolver el problema.

  • Después de la llamada a ClearContent, llamo a ClearHeaders();
  • No estoy especificando una longitud.
  • No estoy especificando en línea en la Disposición
  • Sé que todos dicen que no lo haga, pero tengo un Response.Flush(); seguido por Response.Close();

Y, otra cosa, comprobar su valor contentType para asegurarse de que es correcta para archivos PDF (("application/pdf").

1

este fragmento lo hizo por mí:

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

Las otras respuestas copiar el contenido del archivo en la memoria antes de enviar la respuesta. Si los datos ya están en la memoria, tendrá dos copias, lo que no es muy bueno para la escalabilidad. Esto puede funcionar mejor lugar:

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

Dado que una corriente es una colección de bytes, no hay necesidad de utilizar un BinaryReader. Y siempre que la secuencia de entrada finalice al final del archivo, puede usar el método CopyTo() en la transmisión que desea enviar al navegador web. Todos los contenidos se escribirán en la secuencia de destino, sin hacer copias intermedias de los datos.

Si sólo necesita copiar un cierto número de bytes de la corriente, se puede crear un método de extensión que añade un par más CopyTo() sobrecargas:

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

A continuación, puede usarlo de esta manera:

inputStream.CopyTo(response.OutputStream, contentLength); 

Esto funciona con cualquier flujo de entrada, pero un ejemplo rápido sería la lectura de un archivo del sistema de archivos:

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

Como se mencionó anteriormente, asegúrese de que su tipo MIME sea correcto para el contenido.

0

cuando utiliza su código, está copiando los bytes de su pdf directamente en su respuesta. todos los códigos especiales de Ascii necesitan ser codificados para pasar en http. Al utilizar la función de transmisión, la transmisión se codifica para que no tenga que preocuparse por ello.

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

Este fragmento incluye el código para la lectura en el archivo desde una ruta de archivo, y extraer el nombre del archivo:

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

Y lee todo el archivo en una matriz de bytes y deja el archivo abierto. – CodeCaster

+0

¿Qué cambio recomendaría? Cheers –

+0

¿También necesito llamar a Response.OutputStream.Close()? –

Cuestiones relacionadas