【问题标题】:How to return PDF to browser in MVC?如何在 MVC 中将 PDF 返回到浏览器?
【发布时间】:2010-12-03 09:15:08
【问题描述】:

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

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

【问题讨论】:

标签: c# asp.net asp.net-mvc pdf itextsharp


【解决方案1】:

返回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,您需要先关闭文档并刷新流。
  • Geoff,我正在尝试实现这一目标,但遇到了类似的问题。我在运行时收到错误“无法访问已关闭的流”但如果我不关闭它,则不会返回任何内容。
  • 谢谢@littlechris。你是对的,我已经编辑了代码以包含 pdfWriter.CloseStream = false;
  • 是@Geoff stream.Possition = 0;必填,如果不写,PDF Acrobat 在打开时会抛出错误“文件损坏”
  • 无法将类型“System.Web.Mvc.FileStreamResult”隐式转换为“System.Web.Mvc.FileContentResult”
【解决方案2】:

我可以使用此代码。

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 无法识别。要添加什么命名空间?
  • 我有点担心在我能找到的任何示例中都没有一个 using 语句......这里不需要吗?我认为您至少有 3 个一次性物品...
  • 是的,使用语句很好。如果这是一个生产应用程序,比方说......一个人在使用它,这可能会导致问题......
  • FileSteamResult 将为您关闭流。看到这个答案stackoverflow.com/a/10429907/228770
  • 重要的是设置Position = 0。哈哈。谢谢@TonyBorf
【解决方案3】:

您必须指定:

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

文件在浏览器中直接打开而不是下载

【讨论】:

  • 谢谢!我到处搜索如何做到这一点!
  • 显然,如果你有一个字节数组而不是一个流,你可以使用return File(yourByteArray, "application/pdf");
【解决方案4】:

如果你从你的操作方法中返回一个FileResult,并在控制器上使用File() 扩展方法,那么做你想做的就很容易了。 File() 方法上有覆盖,它将获取文件的二进制内容、文件的路径或 Stream

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

【讨论】:

    【解决方案5】:

    我遇到过类似的问题,并且偶然发现了一个解决方案。我使用了两篇文章,一篇来自stack,展示了返回下载的方法,另一篇来自one,展示了适用于 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
        // https://stackoverflow.com/questions/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");
    }
    

    【讨论】:

    • 优秀的例子!这正是我想要的! -- 皮特 --
    • 用途?关闭?处置?冲洗?谁在乎内存泄漏?
    【解决方案6】:

    FileStreamResult 当然可以。但是如果你看一下Microsoft Docs,它继承自ActionResult -> FileResult,它还有另一个派生类FileContentResult。它“将二进制文件的内容发送到响应”。因此,如果您已经拥有byte[],则应该改用FileContentResult

    public ActionResult DisplayPDF()
    {
        byte[] byteArray = GetPdfFromWhatever();
    
        return new FileContentResult(byteArray, "application/pdf");
    }
    

    【讨论】:

      【解决方案7】:

      您可以创建自定义类来修改内容类型并将文件添加到响应中。

      http://haacked.com/archive/2008/05/10/writing-a-custom-file-download-action-result-for-asp.net-mvc.aspx

      【讨论】:

      • 如该博客文章顶部所述,FileResult 随 Asp.Net MVC 开箱即用,因此不再需要自己编写代码。
      【解决方案8】:

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

      我想像平常一样使用 Razor 创建我的视图/模型,并将它们呈现为 Pdfs

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

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

      https://github.com/andyhutch77/MvcRazorToPdf

      Install-Package MvcRazorToPdf
      

      【讨论】:

        【解决方案9】:

        您通常会执行 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();
        

        【讨论】:

          【解决方案10】:
          HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");
          

          如果文件名是动态生成的,那么这里如何定义文件名,这里是通过guid生成的。

          【讨论】:

            【解决方案11】:

            如果您从 DB 返回 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
                    }
            

            【讨论】:

              猜你喜欢
              • 2015-04-02
              • 1970-01-01
              • 2017-03-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-24
              • 2013-01-23
              • 2021-12-19
              相关资源
              最近更新 更多