Appel à wkhtmltopdf pour générer du PDF à partir de HTML

j'essaie de créer un fichier PDF à partir d'un fichier HTML. Après un peu de recherche, j'ai trouvé: wkhtmltopdf pour être parfait. J'ai besoin de l'appeler .exe de la ASP.NET serveur. J'ai essayé:

    Process p = new Process();
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.FileName = HttpContext.Current.Server.MapPath("wkhtmltopdf.exe");
    p.StartInfo.Arguments = "TestPDF.htm TestPDF.pdf";
    p.Start();
    p.WaitForExit();

sans aucun succès des fichiers créés sur le serveur. Quelqu'un peut-il me donner un pointeur dans la bonne direction? J'ai mis le wkhtmltopdf.fichier exe dans le répertoire supérieur du site. Est-il partout ailleurs, il devrait être tenu?


modifier: si quelqu'un a de meilleures solutions pour créer dynamiquement des fichiers pdf à partir de html, s'il vous plaît laissez-moi savoir.

51
demandé sur Ryan Gates 2009-08-26 05:35:55

10 réponses

mise à jour:

Ma réponse ci-dessous, crée le fichier pdf sur le disque. J'ai ensuite transmis ce fichier au navigateur des utilisateurs en téléchargement. Envisagez d'utiliser quelque chose comme la réponse de Hath ci - dessous pour obtenir wkhtml2pdf à la sortie d'un flux à la place et ensuite envoyer directement à l'utilisateur-qui permettra de contourner beaucoup de problèmes avec les permissions de fichiers, etc.

ma réponse originale:

Assurer vous avez spécifié un chemin de sortie pour le PDF qui peut être écrit par le ASP.NET processus de IIS tournant sur votre serveur (habituellement NETWORK_SERVICE je pense).

Mine ressemble à ceci (et il fonctionne):

/// <summary>
/// Convert Html page at a given URL to a PDF file using open-source tool wkhtml2pdf
/// </summary>
/// <param name="Url"></param>
/// <param name="outputFilename"></param>
/// <returns></returns>
public static bool HtmlToPdf(string Url, string outputFilename)
{
    // assemble destination PDF file name
    string filename = ConfigurationManager.AppSettings["ExportFilePath"] + "\" + outputFilename + ".pdf";

    // get proj no for header
    Project project = new Project(int.Parse(outputFilename));

    var p = new System.Diagnostics.Process();
    p.StartInfo.FileName = ConfigurationManager.AppSettings["HtmlToPdfExePath"];

    string switches = "--print-media-type ";
    switches += "--margin-top 4mm --margin-bottom 4mm --margin-right 0mm --margin-left 0mm ";
    switches += "--page-size A4 ";
    switches += "--no-background ";
    switches += "--redirect-delay 100";

    p.StartInfo.Arguments = switches + " " + Url + " " + filename;

    p.StartInfo.UseShellExecute = false; // needs to be false in order to redirect output
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    p.StartInfo.RedirectStandardInput = true; // redirect all 3, as it should be all 3 or none
    p.StartInfo.WorkingDirectory = StripFilenameFromFullPath(p.StartInfo.FileName);

    p.Start();

    // read the output here...
    string output = p.StandardOutput.ReadToEnd(); 

    // ...then wait n milliseconds for exit (as after exit, it can't read the output)
    p.WaitForExit(60000); 

    // read the exit code, close process
    int returnCode = p.ExitCode;
    p.Close(); 

    // if 0 or 2, it worked (not sure about other values, I want a better way to confirm this)
    return (returnCode == 0 || returnCode == 2);
}
51
répondu MGOwen 2011-06-16 06:29:48

j'ai eu le même problème quand j'ai essayé d'utiliser msmq avec un service windows, mais il était très lent pour une raison quelconque. (le processus).

C'est ce qui a finalement fonctionné:

private void DoDownload()
{
    var url = Request.Url.GetLeftPart(UriPartial.Authority) + "/CPCDownload.aspx?IsPDF=False?UserID=" + this.CurrentUser.UserID.ToString();
    var file = WKHtmlToPdf(url);
    if (file != null)
    {
        Response.ContentType = "Application/pdf";
        Response.BinaryWrite(file);
        Response.End();
    }
}

public byte[] WKHtmlToPdf(string url)
{
    var fileName = " - ";
    var wkhtmlDir = "C:\Program Files\wkhtmltopdf\";
    var wkhtml = "C:\Program Files\wkhtmltopdf\wkhtmltopdf.exe";
    var p = new Process();

    p.StartInfo.CreateNoWindow = true;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    p.StartInfo.RedirectStandardInput = true;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.FileName = wkhtml;
    p.StartInfo.WorkingDirectory = wkhtmlDir;

    string switches = "";
    switches += "--print-media-type ";
    switches += "--margin-top 10mm --margin-bottom 10mm --margin-right 10mm --margin-left 10mm ";
    switches += "--page-size Letter ";
    p.StartInfo.Arguments = switches + " " + url + " " + fileName;
    p.Start();

    //read output
    byte[] buffer = new byte[32768];
    byte[] file;
    using(var ms = new MemoryStream())
    {
        while(true)
        {
            int read =  p.StandardOutput.BaseStream.Read(buffer, 0,buffer.Length);

            if(read <=0)
            {
                break;
            }
            ms.Write(buffer, 0, read);
        }
        file = ms.ToArray();
    }

    // wait or exit
    p.WaitForExit(60000);

    // read the exit code, close process
    int returnCode = p.ExitCode;
    p.Close();

    return returnCode == 0 ? file : null;
}

merci à Graham Ambrose et à tous les autres.

40
répondu Hath 2010-09-10 10:07:56

OK, donc c'est une vieille question, mais un excellent. Et comme je n'ai pas trouvé de bonne réponse, j'ai fait le mien :) aussi, j'ai posté ce projet super simple à GitHub.

voici un exemple de code:

var pdfData = HtmlToXConverter.ConvertToPdf("<h1>SOO COOL!</h1>");

voici quelques points clés:

  • Pas De P/Invoke
  • pas de création d'un nouveau procédé
  • pas de système de fichiers (tous en RAM)
  • natif .NET DLL avec intellisense, etc.
  • capacité de générer un PDF ou PNG ( HtmlToXConverter.ConvertToPng )
16
répondu Timothy Khouri 2014-12-22 23:33:32

consultez la bibliothèque C# wrapper (en utilisant P / Invoke) pour la bibliothèque wkhtmltopdf: https://github.com/pruiz/WkHtmlToXSharp

7
répondu Jason S 2011-04-04 23:07:26

il y a plusieurs raisons pour lesquelles c'est généralement une mauvaise idée. Comment allez-vous contrôler les exécutables qui sont produits mais qui finissent par vivre en mémoire s'il y a un crash? Qu'en est-il des attaques de déni de service, ou si quelque chose de malveillant entre dans TestPDF.htm?

ma compréhension est que le ASP.NET le compte utilisateur n'aura pas le droit de se connecter localement. Il doit également avoir le bon fichier des autorisations d'accès à l'exécutable et à écrire dans le fichier système. Vous devez modifier la Politique de sécurité locale et laisser le ASP.NET compte d'utilisateur (peut-être ASPNET) se connecter localement (il peut être dans la liste deny par défaut). Ensuite, vous devez éditer les permissions sur le système de fichiers NTFS pour les autres fichiers. Si vous êtes dans un environnement d'hébergement partagé, il peut être impossible d'appliquer la configuration dont vous avez besoin.

la meilleure façon d'utiliser un exécutable externe comme celui-ci est de faire la file d'attente des travaux ASP.NET code et avoir une sorte de service file. Si vous faites cela, vous pourrez vous protéger de toutes sortes de mauvaises choses qui se passe. Les problèmes de maintenance avec la modification du compte utilisateur ne valent pas l'effort à mon avis, et bien que la mise en place d'un service ou d'un emploi prévu est une douleur, c'est juste une meilleure conception. Les ASP.NET page devrait Poller une file d'attente de résultat pour la sortie et vous pouvez présenter l'utilisateur avec une page d'attente. Cela est acceptable dans la plupart des cas.

5
répondu Brian Lyttle 2009-08-26 01:55:26

vous pouvez dire à wkhtmltopdf d'envoyer sa sortie vers sout en spécifiant " - " comme fichier de sortie. Vous pouvez ensuite lire la sortie du processus dans le flux de réponse et d'éviter les problèmes d'autorisations avec l'écriture sur le système de fichiers.

4
répondu Graham Ambrose 2010-03-03 21:52:21

Merci pour la question / réponse / tous les commentaires ci-dessus. Je suis tombé sur cela quand j'écrivais mon propre C# wrapper pour WKHTMLtoPDF et il a répondu à quelques-uns des problèmes que j'avais. J'ai fini par écrire à ce sujet dans un post de blog - qui contient également mon emballage (vous verrez sans doute l ' "inspiration" des entrées ci-dessus se faufilant dans mon code...)

http://icanmakethiswork.blogspot.de/2012/04/making-pdfs-from-html-in-c-using.html

Merci encore les gars!

2
répondu John Reilly 2012-04-05 07:52:54

le processus ASP .Net n'a probablement pas d'accès en écriture au répertoire.

essayez de lui dire d'écrire à %TEMP% , et voyez si ça marche.

aussi, faites en sorte que votre page ASP .Net fasse écho aux processus stdout et stderr, et vérifiez les messages d'erreur.

0
répondu SLaks 2009-08-26 01:47:13

généralement, le code de retour =0 est à venir si le fichier pdf est créé correctement et correctement.Si elle n'est pas créée alors la valeur est dans la gamme de-ve.

0
répondu Sukanya 2011-12-17 06:33:35
using System;
using System.Diagnostics;
using System.Web;

public partial class pdftest : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    private void fn_test()
    {
        try
        {
            string url = HttpContext.Current.Request.Url.AbsoluteUri;
            Response.Write(url);
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = 
                @"C:\PROGRA~1\WKHTML~1\wkhtmltopdf.exe";//"wkhtmltopdf.exe";
            startInfo.Arguments = url + @" C:\test"
                 + Guid.NewGuid().ToString() + ".pdf";
            Process.Start(startInfo);
        }
        catch (Exception ex)
        {
            string xx = ex.Message.ToString();
            Response.Write("<br>" + xx);
        }
    }
    protected void btn_test_Click(object sender, EventArgs e)
    {
        fn_test();
    }
}
-1
répondu Peter 2010-03-03 21:54:31