¿Cómo devolver PDF al navegador en MVC?

120

Tengo este código de demostración para iTextSharp

    Document document = new Document();
    try
    {
        PdfWriter.GetInstance(document, new FileStream("Chap0101.pdf", FileMode.Create));

        document.Open();

        document.Add(new Paragraph("Hello World"));

    }
    catch (DocumentException de)
    {
        Console.Error.WriteLine(de.Message);
    }
    catch (IOException ioe)
    {
        Console.Error.WriteLine(ioe.Message);
    }

    document.Close();

¿Cómo consigo que el controlador devuelva el documento pdf al navegador?

EDITAR:

Al ejecutar este código, se abre Acrobat, pero aparece el mensaje de error "El archivo está dañado y no se pudo reparar".

  public FileStreamResult pdf()
    {
        MemoryStream m = new MemoryStream();
        Document document = new Document();
        PdfWriter.GetInstance(document, m);
        document.Open();
        document.Add(new Paragraph("Hello World"));
        document.Add(new Paragraph(DateTime.Now.ToString()));
        m.Position = 0;

        return File(m, "application/pdf");
    }

¿Alguna idea de por qué esto no funciona?

Tony Borf
fuente
- echa un vistazo a nyveldt.com/blog/post/Introducing-RazorPDF
mg1075
@ mg1075 su enlace está muerto
thecoolmacdude

Respuestas:

128

Regresar a FileContentResult. La última línea en la acción de su controlador sería algo como:

return File("Chap0101.pdf", "application/pdf");

Si está generando este PDF dinámicamente, puede ser mejor usar a MemoryStreamy crear el documento en la memoria en lugar de guardarlo en un archivo. El código sería algo como:

Document document = new Document();

MemoryStream stream = new MemoryStream();

try
{
    PdfWriter pdfWriter = PdfWriter.GetInstance(document, stream);
    pdfWriter.CloseStream = false;

    document.Open();
    document.Add(new Paragraph("Hello World"));
}
catch (DocumentException de)
{
    Console.Error.WriteLine(de.Message);
}
catch (IOException ioe)
{
    Console.Error.WriteLine(ioe.Message);
}

document.Close();

stream.Flush(); //Always catches me out
stream.Position = 0; //Not sure if this is required

return File(stream, "application/pdf", "DownloadName.pdf");
Geoff
fuente
@Tony, primero debes cerrar el documento y vaciar el flujo.
Geoff
2
Geoff, estoy tratando de lograr esto, pero tengo problemas similares. Recibo un error en tiempo de ejecución "No se puede acceder a una secuencia cerrada", pero si no la cierro, no se devuelve nada.
littlechris
1
Gracias @littlechris. Tiene razón, he editado el código para incluir pdfWriter.CloseStream = false;
Geoff
1
Sí @Geoff stream.Possition = 0; Se requiere, si no lo escribe, al momento de abrir el PDF Acrobat arroja un error "Archivo dañado"
Alberto León
3
No se puede convertir implícitamente el tipo 'System.Web.Mvc.FileStreamResult' a 'System.Web.Mvc.FileContentResult'
CountMurphy
64

Lo tengo funcionando con este código.

using iTextSharp.text;
using iTextSharp.text.pdf;

public FileStreamResult pdf()
{
    MemoryStream workStream = new MemoryStream();
    Document document = new Document();
    PdfWriter.GetInstance(document, workStream).CloseStream = false;

    document.Open();
    document.Add(new Paragraph("Hello World"));
    document.Add(new Paragraph(DateTime.Now.ToString()));
    document.Close();

    byte[] byteInfo = workStream.ToArray();
    workStream.Write(byteInfo, 0, byteInfo.Length);
    workStream.Position = 0;

    return new FileStreamResult(workStream, "application/pdf");    
}
Tony Borf
fuente
Document, PdfWriter y Paragraph no se reconocen. ¿Qué espacio de nombres se agregará?
Michael
9
Me preocupa un poco que no haya una sola usingdeclaración en ningún ejemplo que pueda encontrar ... ¿No es necesario aquí? Creo que tienes al menos 3 objetos desechables ...
Kobi
Sí, usar declaraciones es bueno. Si esta es una aplicación de producción con más de, digamos ... UNA persona usándola, esto puede causar problemas ...
vbullinger
7
FileSteamResult cerrará la transmisión por usted. Vea esta respuesta stackoverflow.com/a/10429907/228770
Ed Spencer
Lo importante es poner Posición = 0. jaja. gracias @TonyBorf
ThanhLD
23

Debes especificar:

Response.AppendHeader("content-disposition", "inline; filename=file.pdf");
return new FileStreamResult(stream, "application/pdf")

Para que el archivo se abra directamente en el navegador en lugar de descargarlo

Machinegon
fuente
¡Gracias! ¡¡Estaba buscando por todas partes cómo hacer esto !!
Scottie
17

Si devuelve un FileResultde su método de acción y usa el File()método de extensión en el controlador, hacer lo que quiera es bastante fácil. Hay modificaciones en el File()método que tomará el contenido binario del archivo, la ruta al archivo o un Stream.

public FileResult DownloadFile()
{
    return File("path\\to\\pdf.pdf", "application/pdf");
}
NerdFury
fuente
11

Me he encontrado con problemas similares y me he encontrado con una solución. He utilizado dos puestos, uno de pila que muestra el método para volver para su descarga y otro uno que muestra una solución de trabajo para iTextSharp y MVC.

public FileStreamResult About()
{
    // Set up the document and the MS to write it to and create the PDF writer instance
    MemoryStream ms = new MemoryStream();
    Document document = new Document(PageSize.A4.Rotate());
    PdfWriter writer = PdfWriter.GetInstance(document, ms);

    // Open the PDF document
    document.Open();

    // Set up fonts used in the document
    Font font_heading_1 = FontFactory.GetFont(FontFactory.TIMES_ROMAN, 19, Font.BOLD);
    Font font_body = FontFactory.GetFont(FontFactory.TIMES_ROMAN, 9);

    // Create the heading paragraph with the headig font
    Paragraph paragraph;
    paragraph = new Paragraph("Hello world!", font_heading_1);

    // Add a horizontal line below the headig text and add it to the paragraph
    iTextSharp.text.pdf.draw.VerticalPositionMark seperator = new iTextSharp.text.pdf.draw.LineSeparator();
    seperator.Offset = -6f;
    paragraph.Add(seperator);

    // Add paragraph to document
    document.Add(paragraph);

    // Close the PDF document
    document.Close();

    // Hat tip to David for his code on stackoverflow for this bit
    // /programming/779430/asp-net-mvc-how-to-get-view-to-generate-pdf
    byte[] file = ms.ToArray();
    MemoryStream output = new MemoryStream();
    output.Write(file, 0, file.Length);
    output.Position = 0;

    HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");


    // Return the output stream
    return File(output, "application/pdf"); //new FileStreamResult(output, "application/pdf");
}
littlechris
fuente
¡Excelente ejemplo! ¡Esto era exactamente lo que estaba buscando! - Pete -
DigiOz Multimedia
2
Usos? ¿Cerca? ¿Disponer? ¿Enjuagar? ¿A quién le importan las pérdidas de memoria?
vbullinger
3

Sé que esta pregunta es antigua, pero pensé en compartirla ya que no pude encontrar nada similar.

Quería crear mis vistas / modelos de forma normal usando Razor y renderizarlos como PDF .

De esta manera tuve control sobre la presentación en pdf usando la salida html estándar en lugar de averiguar cómo diseñar el documento usando iTextSharp.

El proyecto y el código fuente están disponibles aquí con las instrucciones de instalación de nuget:

https://github.com/andyhutch77/MvcRazorToPdf

Install-Package MvcRazorToPdf
hutchonoid
fuente
3

FileStreamResultciertamente funciona. Pero si nos fijamos en Microsoft Docs , hereda ActionResult -> FileResult, que tiene otra clase derivada FileContentResult. "Envía el contenido de un archivo binario a la respuesta". Entonces, si ya tiene el byte[], debería usarlo FileContentResulten su lugar.

public ActionResult DisplayPDF()
{
    byte[] byteArray = GetPdfFromWhatever();

    return new FileContentResult(byteArray, "application/pdf");
}
Weihui Guo
fuente
2

Normalmente haría un Response.Flush seguido de un Response.Close, pero por alguna razón a la biblioteca iTextSharp no parece gustarle esto. Los datos no pasan y Adobe cree que el PDF está dañado. Deje fuera la función Response.Close y vea si sus resultados son mejores:

Response.Clear();
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-disposition", "attachment; filename=file.pdf"); // open in a new window
Response.OutputStream.Write(outStream.GetBuffer(), 0, outStream.GetBuffer().Length);
Response.Flush();

// For some reason, if we close the Response stream, the PDF doesn't make it through
//Response.Close();
JML
fuente
2
HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");

si el nombre de archivo se genera dinámicamente, entonces cómo definir el nombre de archivo aquí, se genera a través de guid aquí.

SJLee
fuente
1

si devuelve datos var-binary desde DB para mostrar PDF en la ventana emergente o en el navegador, siga este código: -

Ver pagina:

@using (Html.BeginForm("DisplayPDF", "Scan", FormMethod.Post))
    {
        <a href="javascript:;" onclick="document.forms[0].submit();">View PDF</a>
    }

Controlador de escaneo:

public ActionResult DisplayPDF()
        {
            byte[] byteArray = GetPdfFromDB(4);
            MemoryStream pdfStream = new MemoryStream();
            pdfStream.Write(byteArray, 0, byteArray.Length);
            pdfStream.Position = 0;
            return new FileStreamResult(pdfStream, "application/pdf");
        }

        private byte[] GetPdfFromDB(int id)
        {
            #region
            byte[] bytes = { };
            string constr = System.Configuration.ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
            using (SqlConnection con = new SqlConnection(constr))
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.CommandText = "SELECT Scan_Pdf_File FROM PWF_InvoiceMain WHERE InvoiceID=@Id and Enabled = 1";
                    cmd.Parameters.AddWithValue("@Id", id);
                    cmd.Connection = con;
                    con.Open();
                    using (SqlDataReader sdr = cmd.ExecuteReader())
                    {
                        if (sdr.HasRows == true)
                        {
                            sdr.Read();
                            bytes = (byte[])sdr["Scan_Pdf_File"];
                        }
                    }
                    con.Close();
                }
            }

            return bytes;
            #endregion
        }
Ethiraj
fuente