135

I have this demo code for 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();

How do I get the controller to return the pdf document to the browser?

EDIT:

Running this code does open Acrobat but I get an error message "The file is damaged and could not be repaired"

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

Any ideas why this does not work?

hutchonoid
  • 32,982
  • 15
  • 99
  • 104
Tony Borf
  • 4,579
  • 8
  • 43
  • 51

11 Answers11

137

Return a FileContentResult. The last line in your controller action would be something like:

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

If you are generating this PDF dynamically, it may be better to use a MemoryStream, and create the document in memory instead of saving to file. The code would be something like:

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");
Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
Geoff
  • 4,676
  • 3
  • 26
  • 38
  • @Tony, you need to close the document first and flush the stream. – Geoff Oct 03 '09 at 08:49
  • 2
    Geoff, I 'm try to achieve this, but having similar problems. I get an error at run time "Cannot access a closed Stream" But if I don't close it nothing is returned. – littlechris Nov 04 '09 at 15:48
  • 1
    Thanks @littlechris. You are right, I have edited the code to inclued pdfWriter.CloseStream = false; – Geoff Nov 04 '09 at 17:09
  • 1
    Yes @Geoff stream.Possition = 0; is required, if you do not write it, at the moment of open the PDF Acrobat throws an error "File damaged" – Alberto León Jul 18 '12 at 14:46
  • 3
    Cannot implicitly convert type 'System.Web.Mvc.FileStreamResult' to 'System.Web.Mvc.FileContentResult' – CountMurphy Apr 13 '15 at 16:14
  • @Geoff thanks for this - the `stream.flush();` and `stream.Position = 0;` are both required. Yours is the only example I found that had these.... Without these lines an empty document is returned. – Percy May 19 '16 at 09:21
  • @Rick `stream.Flush()` is useless with MemoryStream, but necessary with other kinds of streams. From documentation : "Overrides the Stream.Flush method so that no action is performed." – Julien N Aug 02 '16 at 12:53
  • I think for the first example you give, the return type should be FilePathResult. – Jeff Reddy Nov 06 '19 at 17:23
72

I got it working with this code.

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
  • 4,579
  • 8
  • 43
  • 51
  • 1
    Document,PdfWriter and Paragraph are unrecognized .What namespace to be added? – michael Jul 28 '11 at 06:46
  • 11
    I am a little concerned there is not a single `using` statement in any example I can find... Is it not needed here? I think you have at least 3 disposable objects... – Kobi Feb 08 '12 at 16:32
  • Yes, using statements are good. If this is a production app with more than, say... ONE person using it, this may cause problems... – vbullinger Sep 25 '12 at 18:48
  • 7
    The FileSteamResult will close the stream for you. See this answer http://stackoverflow.com/a/10429907/228770 – Ed Spencer Apr 23 '14 at 07:46
  • The important thing is set Position = 0. haha. thank you @TonyBorf – ThanhLD Nov 08 '18 at 03:35
30

You must specify :

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

For the file to be opened directly in the browser instead of being downloaded

Machinegon
  • 1,855
  • 1
  • 28
  • 45
19

If you return a FileResult from your action method, and use the File() extension method on the controller, doing what you want is pretty easy. There are overrides on the File() method that will take the binary contents of the file, the path to the file, or a Stream.

public FileResult DownloadFile()
{
    return File("path\\to\\pdf.pdf", "application/pdf");
}
NerdFury
  • 18,876
  • 5
  • 38
  • 41
11

I've run into similar problems and I've stumbled accross a solution. I used two posts, one from stack that shows the method to return for download and another one that shows a working solution for ItextSharp and 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");
}
Community
  • 1
  • 1
littlechris
  • 4,174
  • 10
  • 43
  • 66
7

FileStreamResult certainly works. But if you look at the Microsoft Docs, it inherits from ActionResult -> FileResult, which has another derived class FileContentResult. It "sends the contents of a binary file to the response". So if you already have the byte[], you should just use FileContentResult instead.

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

    return new FileContentResult(byteArray, "application/pdf");
}
Weihui Guo
  • 3,669
  • 5
  • 34
  • 56
3

You can create a custom class to modify the content type and add the file to the response.

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

Chris Kooken
  • 32,730
  • 15
  • 85
  • 123
  • 7
    As stated at the top of that blog post, the FileResult comes out of the box with Asp.Net MVC, so coding your own is no longer necessary. – NerdFury Oct 06 '09 at 04:28
3

I know this question is old but I thought I would share this as I could not find anything similar.

I wanted to create my views/models as normal using Razor and have them rendered as Pdfs.

This way I had control over the pdf presentation using standard html output rather than figuring out how to layout the document using iTextSharp.

The project and source code is available here with nuget installation instructions:

https://github.com/andyhutch77/MvcRazorToPdf

Install-Package MvcRazorToPdf
hutchonoid
  • 32,982
  • 15
  • 99
  • 104
2

You would normally do a Response.Flush followed by a Response.Close, but for some reason the iTextSharp library doesn't seem to like this. The data doesn't make it through and Adobe thinks the PDF is corrupt. Leave out the Response.Close function and see if your results are better:

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
  • 409
  • 2
  • 8
2
HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");

if the filename is generating dynamically then how to define filename here, it is generating through guid here.

S.J.Lee
  • 23
  • 8
1

if you return var-binary data from DB to display PDF on popup or browser means follow this code:-

View page:

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

Scan controller:

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
  • 67
  • 11