Comment renvoyer le PDF au navigateur dans MVC?

120

J'ai ce code de démonstration pour 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();

Comment puis-je demander au contrôleur de renvoyer le document PDF au navigateur?

ÉDITER:

L'exécution de ce code ouvre Acrobat mais j'obtiens un message d'erreur "Le fichier est endommagé et n'a pas pu être réparé"

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

Des idées pourquoi cela ne fonctionne pas?

Tony Borf
la source
@ mg1075 votre lien est mort
thecoolmacdude

Réponses:

128

Retourner un FileContentResult. La dernière ligne de votre action de contrôleur serait quelque chose comme:

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

Si vous générez ce PDF dynamiquement, il peut être préférable d'utiliser a MemoryStreamet de créer le document en mémoire au lieu de l'enregistrer dans un fichier. Le code serait quelque chose comme:

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
la source
@Tony, vous devez d'abord fermer le document et vider le flux.
Geoff
2
Geoff, j'essaye d'y parvenir, mais j'ai des problèmes similaires. J'obtiens une erreur au moment de l'exécution "Impossible d'accéder à un flux fermé" Mais si je ne le ferme pas, rien n'est renvoyé.
littlechris
1
Merci @littlechris. Vous avez raison, j'ai édité le code pour inclure pdfWriter.CloseStream = false;
Geoff
1
Oui @Geoff stream.Possition = 0; est requis, si vous ne l'écrivez pas, au moment de l'ouverture du PDF Acrobat jette une erreur "Fichier endommagé"
Alberto León
3
Impossible de convertir implicitement le type «System.Web.Mvc.FileStreamResult» en «System.Web.Mvc.FileContentResult»
CountMurphy
64

Je l'ai fait fonctionner avec ce 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
la source
Document, PdfWriter et Paragraph ne sont pas reconnus. Quel espace de noms ajouter?
michael
9
Je suis un peu inquiet qu'il n'y ait pas une seule usingdéclaration dans aucun exemple que je puisse trouver ... N'est-ce pas nécessaire ici? Je pense que vous avez au moins 3 objets jetables ...
Kobi
Oui, l'utilisation d'instructions est bonne. S'il s'agit d'une application de production avec plus de, disons ... UNE personne qui l'utilise, cela peut causer des problèmes ...
vbullinger
7
Le FileSteamResult fermera le flux pour vous. Voir cette réponse stackoverflow.com/a/10429907/228770
Ed Spencer
L'important est de définir Position = 0. haha. merci @TonyBorf
ThanhLD
23

Vous devez spécifier:

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

Pour que le fichier soit ouvert directement dans le navigateur au lieu d'être téléchargé

Machinegon
la source
Je vous remercie! Je cherchais partout comment faire cela !!
Scottie le
17

Si vous retournez un FileResultde votre méthode d'action et utilisez la File()méthode d'extension sur le contrôleur, faire ce que vous voulez est assez facile. Il existe des substitutions sur la File()méthode qui prendra le contenu binaire du fichier, le chemin d'accès au fichier ou un fichier Stream.

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

J'ai rencontré des problèmes similaires et je suis tombé sur une solution. J'ai utilisé deux articles, un de la pile qui montre la méthode à retourner pour le téléchargement et un autre qui montre une solution de travail pour ItextSharp et 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
la source
Excellent exemple! C'était exactement ce que je cherchais! - Pete -
DigiOz Multimedia
2
Utilisations? Fermer? Disposer? Affleurer? Qui se soucie des fuites de mémoire?
vbullinger
3

Je sais que cette question est ancienne mais je pensais que je la partagerais car je ne pouvais rien trouver de similaire.

Je voulais créer mes vues / modèles comme d'habitude en utilisant Razor et les rendre au format PDF .

De cette façon, j'avais le contrôle sur la présentation PDF en utilisant la sortie html standard plutôt que de comprendre comment mettre en page le document en utilisant iTextSharp.

Le projet et le code source sont disponibles ici avec les instructions d'installation de nuget:

https://github.com/andyhutch77/MvcRazorToPdf

Install-Package MvcRazorToPdf
hutchonoïde
la source
3

FileStreamResultfonctionne certainement. Mais si vous regardez le Microsoft Docs , il hérite de ActionResult -> FileResult, qui a une autre classe dérivée FileContentResult. Il "envoie le contenu d'un fichier binaire à la réponse". Donc, si vous avez déjà le byte[], vous devriez simplement l'utiliser à la FileContentResultplace.

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

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

Vous feriez normalement un Response.Flush suivi d'un Response.Close, mais pour une raison quelconque, la bibliothèque iTextSharp ne semble pas aimer ça. Les données ne parviennent pas à passer et Adobe pense que le PDF est corrompu. Oubliez la fonction Response.Close et voyez si vos résultats sont meilleurs:

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

si le nom de fichier est généré dynamiquement alors comment définir le nom de fichier ici, il est généré via guid ici.

SJLee
la source
1

si vous retournez des données var-binary de la base de données pour afficher le PDF sur une fenêtre contextuelle ou un navigateur, suivez ce code: -

Voir page:

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

Contrôleur de scan:

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
la source