54 votes

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

Je suis d'essayer de créer un fichier PDF à partir d'un fichier HTML. Après en cherchant un peu j'ai trouvé: wkhtmltopdf d'être parfait. J'ai besoin de l'appeler .exe à partir de la ASP.NET serveur. J'ai tenté:

    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 le succès de tous les fichiers créés sur le serveur. Quelqu'un peut-il me donner un pointeur dans la bonne direction? J'ai mis le wkhtmltopdf.exe fichier dans le répertoire de niveau supérieur du site. Est-il partout ailleurs, il devrait être tenu?

Merci.


Edit: 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.

51voto

MGOwen Points 678

Mise à jour:
Ma réponse ci-dessous, crée le fichier pdf sur le disque. J'ai ensuite diffusé ce fichier dans le navigateur d'utilisateurs en téléchargement. Envisagez d'utiliser quelque chose comme A la réponse ci-dessous pour obtenir wkhtml2pdf pour la sortie d'un flux à la place, puis les envoyer directement à l'utilisateur qui permettra de contourner beaucoup de problèmes avec les autorisations de fichier, etc.

Ma réponse originale à cette question:
Assurez-vous que vous avez spécifié un chemin de sortie pour le format PDF, qui est accessible en écriture par l'ASP.NET processus de IIS qui s'exécute sur votre serveur (généralement NETWORK_SERVICE je pense).

Le mien ressemble à ça (et ça marche):

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

41voto

Hath Points 5505

J'ai eu le même problème quand j'ai essayé d'utiliser msmq avec un service Windows mais c'était très lent pour une raison quelconque. (la partie 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.

7voto

Jason S Points 345

Consultez la bibliothèque d'encapsulation C # (à l'aide de P / Invoke) pour la bibliothèque wkhtmltopdf: https://github.com/pruiz/WkHtmlToXSharp

5voto

Brian Lyttle Points 9344

Il y a beaucoup de raisons pour lesquelles c'est généralement une mauvaise idée. Comment allez-vous contrôler les exécutables qui se donné naissance à off mais à la fin de vie dans la mémoire si il y a un blocage? Ce sujet de déni de service attaques, ou si quelque chose de malveillant pénètre dans TestPDF.htm?

Ma compréhension est que le ASP.NET compte d'utilisateur n'aura pas les droits d'ouverture de session localement. Il doit également avoir le bon fichier des autorisations d'accès à l'exécutable et à écrire dans le fichier système. Vous avez besoin de modifier la stratégie de sécurité locale et de laisser le ASP.NET compte d'utilisateur (peut-être ASPNET) ouverture d'une session locale (il peut être dans la liste d'exclusion par défaut). Ensuite, vous devez modifier les autorisations 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 c'est à la file d'attente de tâches à partir de la ASP.NET code et avoir une sorte de moniteur de service de la file d'attente. Si vous faites cela, vous pourrez vous protéger de toutes sortes de mauvaises choses qui se passe. Les problèmes d'entretien, de modifier le compte d'utilisateur ne sont pas en vaut la peine à mon avis, et tandis que la configuration d'un service ou d'un travail planifié est une douleur, c'est juste une meilleure conception. L'ASP.NET la page doit interroger à la suite de la file d'attente pour la sortie et vous pouvez vous présenter à l'utilisateur d'attendre de la page. Cela est acceptable dans la plupart des cas.

5voto

Graham Ambrose Points 586

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X