Wie kann ich PDF in MVC an den Browser zurückgeben?

120

Ich habe diesen Demo-Code für 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();

Wie bringe ich den Controller dazu, das PDF-Dokument an den Browser zurückzugeben?

BEARBEITEN:

Durch Ausführen dieses Codes wird Acrobat geöffnet, es wird jedoch die Fehlermeldung "Die Datei ist beschädigt und konnte nicht repariert werden" angezeigt.

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

Irgendwelche Ideen, warum das nicht funktioniert?

Tony Borf
quelle
- Überprüfen Sie nyveldt.com/blog/post/Introducing-RazorPDF
mg1075
@ mg1075 Ihr Link ist tot
thecoolmacdude

Antworten:

128

Rückgabe a FileContentResult. Die letzte Zeile in Ihrer Controller-Aktion wäre ungefähr so:

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

Wenn Sie diese PDF-Datei dynamisch generieren, ist es möglicherweise besser, a zu verwenden MemoryStreamund das Dokument im Speicher zu erstellen, als es in einer Datei zu speichern. Der Code wäre ungefähr so:

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
quelle
@ Tony, Sie müssen zuerst das Dokument schließen und den Stream leeren.
Geoff
2
Geoff, ich versuche das zu erreichen, habe aber ähnliche Probleme. Zur Laufzeit wird die Fehlermeldung "Kein Zugriff auf einen geschlossenen Stream möglich" angezeigt. Wenn ich ihn jedoch nicht schließe, wird nichts zurückgegeben.
Littlechris
1
Danke @littlechris. Sie haben Recht, ich habe den Code in pdfWriter.CloseStream = false bearbeitet.
Geoff
1
Ja @Geoff stream.Possition = 0; Wenn Sie es nicht schreiben, wird beim Öffnen des PDF-Acrobat ein Fehler "Datei beschädigt"
Alberto León
3
Typ 'System.Web.Mvc.FileStreamResult' kann nicht implizit in 'System.Web.Mvc.FileContentResult'
konvertiert werden
64

Ich habe es mit diesem Code zum Laufen gebracht.

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
quelle
Dokument, PdfWriter und Absatz werden nicht erkannt. Welcher Namespace muss hinzugefügt werden?
Michael
9
Ich bin ein wenig besorgt, dass es usingin keinem Beispiel eine einzige Aussage gibt, die ich finden kann ... Wird sie hier nicht benötigt? Ich denke, Sie haben mindestens 3 Einwegobjekte ...
Kobi
Ja, Aussagen zu verwenden ist gut. Wenn dies eine Produktions-App mit mehr als beispielsweise einer Person ist, die sie verwendet, kann dies zu Problemen führen ...
vbullinger
7
Das FileSteamResult schließt den Stream für Sie. Siehe diese Antwort stackoverflow.com/a/10429907/228770
Ed Spencer
Das Wichtigste ist, Position = 0 zu setzen. Haha. danke @TonyBorf
ThanhLD
23

Sie müssen angeben:

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

Damit die Datei direkt im Browser geöffnet wird, anstatt heruntergeladen zu werden

Machinegon
quelle
Danke dir! Ich habe überall gesucht, wie das geht !!
Scottie
17

Wenn Sie eine FileResultvon Ihrer Aktionsmethode zurückgeben und die File()Erweiterungsmethode auf dem Controller verwenden, ist es ziemlich einfach, das zu tun, was Sie wollen. Es gibt Überschreibungen für die File()Methode, die den binären Inhalt der Datei, den Pfad zur Datei oder a übernimmt Stream.

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

Ich bin auf ähnliche Probleme gestoßen und bin auf eine Lösung gestoßen. Ich habe zwei Beiträge verwendet, einen vom Stapel , der die Methode zum Zurückgeben zum Herunterladen zeigt, und einen anderen , der eine funktionierende Lösung für ItextSharp und MVC zeigt.

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
quelle
Exzellentes Beispiel! Genau das habe ich gesucht! - Pete -
DigiOz Multimedia
2
Verwendungen? Schließen? Entsorgen? Spülen? Wer kümmert sich um Speicherlecks?
Vbullinger
3

Ich weiß, dass diese Frage alt ist, aber ich dachte, ich würde sie teilen, da ich nichts Ähnliches finden konnte.

Ich wollte meine Ansichten / Modelle wie gewohnt mit Razor erstellen und sie als PDFs rendern lassen .

Auf diese Weise hatte ich die Kontrolle über die PDF-Präsentation mithilfe der Standard-HTML-Ausgabe, anstatt herauszufinden, wie das Dokument mit iTextSharp gestaltet werden soll.

Der Projekt- und Quellcode ist hier mit Anweisungen zur Nuget-Installation verfügbar:

https://github.com/andyhutch77/MvcRazorToPdf

Install-Package MvcRazorToPdf
Hutchonoid
quelle
3

FileStreamResultfunktioniert auf jeden Fall. Wenn Sie sich jedoch die Microsoft-Dokumente ansehen , erbt sie von einer ActionResult -> FileResultanderen abgeleiteten Klasse FileContentResult. Es "sendet den Inhalt einer Binärdatei an die Antwort". Wenn Sie das bereits haben byte[], sollten Sie es FileContentResultstattdessen verwenden.

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

    return new FileContentResult(byteArray, "application/pdf");
}
Weihui Guo
quelle
2

Normalerweise würden Sie eine Response.Flush gefolgt von einer Response.Close ausführen, aber aus irgendeinem Grund scheint die iTextSharp-Bibliothek dies nicht zu mögen. Die Daten schaffen es nicht und Adobe hält das PDF für beschädigt. Lassen Sie die Response.Close-Funktion weg und prüfen Sie, ob Ihre Ergebnisse besser sind:

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

Wenn der Dateiname dynamisch generiert wird, wie er hier definiert wird, wird er hier durch guid generiert.

SJLee
quelle
1

Wenn Sie var-binäre Daten aus der Datenbank zurückgeben, um PDF im Popup oder Browser anzuzeigen, befolgen Sie diesen Code: -

Seite anzeigen:

@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
quelle