Como retornar o PDF ao navegador no MVC?


120

Eu tenho este código de demonstração para o 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();

Como faço para que o controlador retorne o documento pdf ao navegador?

EDITAR:

A execução desse código abre o Acrobat, mas recebo uma mensagem de erro "O arquivo está danificado e não pôde ser reparado"

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

Alguma idéia de por que isso não funciona?



@ mg1075 seu link está morto
— thecoolmacdude

Respostas:


128

Retornar a FileContentResult. A última linha na ação do seu controlador seria algo como:

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

Se você estiver gerando este PDF dinamicamente, pode ser melhor usar ae MemoryStreamcriar o documento na memória em vez de salvar em arquivo. O código seria 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");

@ Tony, você precisa fechar o documento primeiro e liberar o fluxo.
— 9303 Geoff

2
Geoff, estou tentando conseguir isso, mas com problemas semelhantes. Eu recebo um erro no tempo de execução "Não é possível acessar um fluxo fechado" Mas se eu não fechar, nada será retornado.
— Littlechris #

1
Obrigado @littlechris. Você está certo, editei o código para incluir pdfWriter.CloseStream = false;
— Geoff

1
Sim @Geoff stream.Possition = 0; é necessária, se você não escrevê-lo, no momento da abertura do PDF Acrobat gera um erro "danificado Arquivo"
— Alberto León

3
Não é possível converter implicitamente o tipo 'System.Web.Mvc.FileStreamResult' para 'System.Web.Mvc.FileContentResult'
— CountMurphy

64

Eu consegui trabalhar com esse 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");    
}

Documento, PdfWriter e Parágrafo não são reconhecidos. Que namespace a ser adicionado?
— 28611 Michael

9
Estou um pouco preocupado que não exista uma única usingdeclaração em nenhum exemplo que eu possa encontrar ... Não é necessário aqui? Acho que você tem pelo menos 3 objetos descartáveis ...
— Kobi

Sim, usar declarações é bom. Se este é um aplicativo de produção com mais de, digamos ... uma pessoa usá-lo, isso pode causar problemas ...
— vbullinger

7
O FileSteamResult fechará o fluxo para você. Veja esta resposta stackoverflow.com/a/10429907/228770
— Ed Spencer

O importante é definir a posição = 0. haha. Obrigado @TonyBorf
— ThanhLD

23

Você deve especificar:

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

Para que o arquivo seja aberto diretamente no navegador em vez de ser baixado


Obrigado! Eu estava procurando em todos os lugares sobre como fazer isso!
— Scottie

17

Se você retornar um FileResultdo seu método de ação e usar o File()método de extensão no controlador, é fácil fazer o que deseja. Existem substituições no File()método que utilizará o conteúdo binário do arquivo, o caminho para o arquivo ou a Stream.

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

11

Encontrei problemas semelhantes e me deparei com uma solução. Eu usei dois postes, um de pilha que mostra o método para retornar para download e outro um que mostre uma solução de trabalho para iTextSharp e 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");
}

Excelente exemplo! Era exatamente isso que eu estava procurando! - Pete -
— DigiOz Multimedia

2
Usos? Perto? Descarte? Rubor? Quem se importa com vazamentos de memória?
— precisa saber é o seguinte


3

Sei que essa pergunta é antiga, mas pensei em compartilhar isso, pois não consegui encontrar nada parecido.

Eu queria criar minhas visualizações / modelos normalmente usando o Razor e processá- las como PDFs .

Dessa forma, eu tinha controle sobre a apresentação em pdf usando a saída html padrão em vez de descobrir como fazer o layout do documento usando o iTextSharp.

O projeto e o código-fonte estão disponíveis aqui com instruções de instalação de nuget:

https://github.com/andyhutch77/MvcRazorToPdf

Install-Package MvcRazorToPdf

3

FileStreamResultcertamente funciona. Mas se você olhar para o Microsoft Docs , ele herdará de ActionResult -> FileResult, que possui outra classe derivada FileContentResult. "Envia o conteúdo de um arquivo binário para a resposta". Portanto, se você já possui o byte[], você deve apenas usar FileContentResult.

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

    return new FileContentResult(byteArray, "application/pdf");
}

2

Você normalmente faria um Response.Flush seguido de um Response.Close, mas por algum motivo a biblioteca iTextSharp não parece gostar disso. Os dados não passam e a Adobe acha que o PDF está corrompido. Deixe de fora a função Response.Close e veja se seus resultados são melhores:

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

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

se o nome do arquivo estiver gerando dinamicamente, como definir o nome do arquivo aqui, será gerado através do guid aqui.


1

se você retornar dados binários do banco de dados para exibir PDF em pop-up ou 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 digitalização:

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
        }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.