如何在MVC中将PDF返回到浏览器?


120

我有这个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();

如何获得控制器以将pdf文档返回到浏览器?

编辑:

运行此代码会打开Acrobat,但出现错误消息“文件已损坏,无法修复”

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

任何想法为什么这不起作用?



@ mg1075您的链接已死
thecoolmacdude

Answers:


128

返回FileContentResult。控制器操作的最后一行是这样的:

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

如果要动态生成此PDF,则最好使用MemoryStream,然后在内存中创建文档而不是保存到文件中。该代码将类似于:

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,您需要先关闭文档并刷新流。
杰夫

2
杰夫,我正在尝试实现这一目标,但是存在类似的问题。运行时出现错误“无法访问关闭的流”,但如果不关闭,则不会返回任何内容。
littlechris

1
谢谢@littlechris。没错,我已将代码编辑为pdfWriter.CloseStream = false;。
Geoff

1
是@Geoff stream.Possition = 0; 是必需的,如果您不编写它,则在打开PDF时,Acrobat会引发错误“文件已损坏”
AlbertoLeón2012年

3
无法隐式转换类型“System.Web.Mvc.FileStreamResult”到“System.Web.Mvc.FileContentResult”
CountMurphy

64

我使用此代码。

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

无法识别Document,PdfWriter和Paragraph。要添加什么名称空间?
迈克尔

9
我有点担心,using在我可以找到的任何示例中都没有一个语句...这里不需要吗?我认为您至少有3个一次性物品...
Kobi 2012年

是的,使用语句很好。如果这是一个生产应用程序,并且具有多个功能,例如...一个人使用它,可能会引起问题...
vbullinger 2012年

7
FileSteamResult将为您关闭流。看到这个答案stackoverflow.com/a/10429907/228770
Ed Spencer

重要的是将Position设置为0。谢谢@TonyBorf
ThanhLD

23

您必须指定:

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

对于文件将直接打开浏览器被代替下载


谢谢!我到处都在搜索如何做!
斯科蒂(Scottie)2013年

17

如果您FileResult从操作方法返回a ,并File()在控制器上使用扩展方法,则执行所需的操作非常容易。该File()方法有一些替代方法,这些方法将获取文件的二进制内容,文件的路径或Stream

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

11

我遇到了类似的问题,偶然发现了一个解决方案。我用了两篇文章,一篇来自堆栈,显示了返回下载的方法,另一篇显示了针对ItextSharp和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");
}

优秀的例子!这正是我想要的!-皮特-
DigiOz多媒体

2
使用?关?处理?冲洗?谁在乎内存泄漏?
vbullinger 2012年


3

我知道这个问题很旧,但我想分享一下,因为找不到类似的东西。

我想使用Razor正常创建视图/模型,并将其渲染为Pdfs

这样,我可以使用标准的html输出控制pdf演示文稿,而不用弄清楚如何使用iTextSharp布局文档。

可在此处获取项目和源代码以及nuget安装说明:

https://github.com/andyhutch77/MvcRazorToPdf

Install-Package MvcRazorToPdf

3

FileStreamResult当然有效。但是,如果您查看Microsoft Docs,它将继承自Microsoft DocsActionResult -> FileResult,后者具有另一个派生类FileContentResult。它“将二进制文件的内容发送到响应”。因此,如果您已经有了byte[],则应该FileContentResult改用。

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

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

2

通常,您会先执行Response.Flush,然后再执行Response.Close,但是由于某些原因,iTextSharp库似乎并不喜欢这样。数据无法通过,Adobe认为PDF已损坏。忽略Response.Close函数,看看您的结果是否更好:

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

如果文件名是动态生成的,那么如何在此处定义文件名,它是通过guid生成的。


1

如果从数据库返回var-binary数据以在弹出窗口或浏览器上显示PDF,则请遵循以下代码:

查看页面:

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

扫描控制器:

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
        }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.