调用wkhtmltopdf从HTML生成PDF

我试图从HTML文件创build一个PDF文件。 看了一下后,我发现: wkhtmltopdf是完美的。 我需要从ASP.NET服务器调用这个.exe。 我试过了:

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(); 

没有在服务器上创build任何文件的成功。 任何人都可以给我一个正确的方向指针? 我把wkhtmltopdf.exe文件放在网站的顶层目录下。 是否还有其他地方呢?


编辑:如果任何人有更好的解决scheme,从HTMLdynamic创buildPDF文件,请让我知道。

更新:
我的答案如下,在磁盘上创buildPDF文件。 然后,我将该文件stream式传输到用户浏览器作为下载。 考虑使用下面的Hath的答案,以获得wkhtml2pdf输出到一个stream,然后直接发送给用户 – 这将绕过许多问题与文件权限等

我原来的答案是:
确保你已经为你的服务器上运行的IIS的ASP.NET进程(通常是NETWORK_SERVICE,我认为)可写入的PDF指定了一个输出path。

我看起来像这样(和它的作品):

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

当我尝试使用msmq与Windows服务,但我有同样的问题,但由于某种原因,它是非常缓慢的。 (过程部分)。

这是最后的工作:

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

感谢Graham Ambrose和其他人。

好的,这是一个老问题,但是一个很好的问题。 而且,因为我没有find一个好的答案,我做了我自己的:) 另外,我已经把这个超级简单的项目发布到GitHub。

这是一些示例代码:

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

这里有一些关键点:

  • 没有P / Invoke
  • 没有创build一个新的过程
  • 没有文件系统(全部在RAM中)
  • 原生.NET DLL,智能感知等
  • 能够生成PDF或PNG( HtmlToXConverter.ConvertToPng

查看wkhtmltopdf库的C#包装库(使用P / Invoke): https : //github.com/pruiz/WkHtmlToXSharp

这通常是一个坏主意,有很多原因。 你将如何控制产生的可执行文件,但如果发生崩溃,最终还是会在内存中生存呢? 怎么样的拒绝服务攻击,或者如果有什么恶意进入TestPDF.htm?

我的理解是,ASP.NET用户帐户将无权login本地。 它还需要具有正确的文件权限才能访问可执行文件并写入文件系统。 您需要编辑本地安全策略并让ASP.NET用户帐户(可能是ASPNET)在本地login(默认情况下,它可能位于拒绝列表中)。 然后,您需要编辑其他文件在NTFS文件系统上的权限。 如果您处于共享主机环境中,则可能无法应用所需的configuration。

像这样使用外部可执行文件的最好方法是从ASP.NET代码中排队作业,并且拥有某种服务来监视队列。 如果你这样做,你会保护自己免受各种不好的事情发生。 更改用户帐户的维护问题在我看来是不值得的,而设置服务或预定的工作是一个痛苦,它只是一个更好的devise。 ASP.NET页面应轮询输出的结果队列,并且可以向用户显示一个等待页面。 在大多数情况下这是可以接受的

您可以通过指定“ – ”作为输出文件来告诉wkhtmltopdf将其输出发送到sout。 然后,您可以将进程的输出读取到响应stream中,并避免写入文件系统时的权限问题。

感谢上面的问题/答案/所有评论。 当我为WKHTMLtoPDF编写自己的C#包装器时遇到了这个问题,它解决了我遇到的一些问题。 我最终在一篇博客文章中写了这篇文章 – 里面还包含了我的包装器(你肯定会看到上面的代码中的“灵感”渗透到我的代码中)

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

再次感谢你们!

ASP .Net进程可能没有对该目录的写入权限。

尝试告诉它写入%TEMP% ,看看它是否工作。

此外,使您的ASP .Net页面回显进程的stdout和stderr,并检查错误消息。

一般来说,返回代码= 0即将到来,如果PDF文件正确和正确创build。如果它没有创build,则值在-ve范围内。

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