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