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?

Tony Borf
fonte
@ 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");
Geoff
fonte
@ 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");    
}
Tony Borf
fonte
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

Machinegon
fonte
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");
}
NerdFury
fonte
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");
}
littlechris
fonte
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
hutchonoid
fonte
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");
}
Weihui Guo
fonte
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();
JML
fonte
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.

SJLee
fonte
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
        }
ethiraj
fonte