我正在开发一个Umbraco内部网站点,我在其中调用wkhtmltopdf.exe来创建一些pdf。那些pdf使用内容的主页面和页眉和页脚的两个额外页面。只要我没有身份验证的网站,事情就会很好。我们希望使用我们的Active Directory帐户登录该站点,因此我启用了Windows身份验证。运行此操作的例程是单击处理页面的按钮,并在浏览器上显示pdf或下载它。在任何情况下,它都是相同的过程。在visual studio中运行调试时,当涉及到代码的第一部分(var p = ...)时,它会抛出异常" Message ="没有进程与此对象关联。&#34 ;因为它无法进行身份验证。我可以看到,当我执行代码并使用visual studio检查器后暂停代码时。该方法运行到最后,但由于我之前提到的错误,它产生一个空白的pdf。如果我硬编码用户名和密码,那么它可以正常工作。
网站正在iis express的本地开发环境中运行。由于我第一次登录时浏览到该站点时启用了Windows身份验证。 Wkhtmltopdf.exe位于本地驱动器 - 它不在网站上。初始设置基于此处描述的方法http://icanmakethiswork.blogspot.se/2012/04/making-pdfs-from-html-in-c-using.html只有属于我们Active Directory域的用户才能访问该网站,但由于我们使用相同的帐户登录到Windows,因此Windows身份验证可以解决问题: )
public static void HtmlToPdf(string outputFilename, string[] urls,
string[] options = null,
bool streamPdf = false,
string pdfHtmlToPdfExePath = "C:\\Program Files (x86)\\wkhtmltopdf\\bin\\wkhtmltopdf.exe")
{
string urlsSeparatedBySpaces = string.Empty;
try
{
//Determine inputs
if ((urls == null) || (urls.Length == 0))
{
throw new Exception("No input URLs provided for HtmlToPdf");
}
urlsSeparatedBySpaces = String.Join(" ", urls); //Concatenate URLs
var p = new System.Diagnostics.Process()
{
StartInfo =
{
FileName = pdfHtmlToPdfExePath,
Arguments = ((options == null) ? "" : String.Join(" ", options)) + " " + urlsSeparatedBySpaces + " -",
UseShellExecute = false, // needs to be false in order to redirect output
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true, // redirect all 3, as it should be all 3 or none
WorkingDirectory = string.Empty
}
};
p.Start();
var output = p.StandardOutput.ReadToEnd();
byte[] buffer = p.StandardOutput.CurrentEncoding.GetBytes(output);
p.WaitForExit(60000);
p.Close();
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = "application/pdf";
if (!streamPdf)
{
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename='" + outputFilename + "'");
}
HttpContext.Current.Response.BinaryWrite(buffer);
HttpContext.Current.Response.End();
}
catch (Exception exc)
{
throw new Exception("Problem generating PDF from HTML, URLs: " + urlsSeparatedBySpaces + ", outputFilename: " + outputFilename, exc);
}
}
我使用LoadUserProfile = true对此进行了测试,但这并没有帮助。在阅读了各种论坛帖子后,我看到的唯一建议的解决方案是强制使用UserName,Password等登录过程。但这很糟糕,因为用户已经登录,我们可以/应该使用像CredentialCache.DefaultCredentials这样的东西。
我来的一个解决方法是在请求中使用DefaultCredentials在本地保存htmls,我可以毫无问题地访问它们并创建pdf,但即使这是一个艰苦的过程,因为我需要创建可打印的CSS和javascripts和下载它们等等。这是我最后一个解决方案,我已经实现了80%,但似乎也很讨厌。以下是我抓取网页的另一个代码示例。
var request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
var response = (HttpWebResponse)request.GetResponse();
var stream = response.GetResponseStream();
总结一下。 Wkhtmltopdf无法对自身进行身份验证,因此它可以抓取所需的页面并将其转换为pdf。是否有任何巧妙的方法使该过程能够使用我登录到站点的当前用户凭据进行身份验证,以便它可以访问这些页面?
答案 0 :(得分:2)
我使用Rotativa作为Wkhtmltopdf的包装。
为了让它在iis上工作,我创建了一个单独的用户帐户,有足够的权限来运行Wkhtmltopdf.exe。然后传递用户名&使用开关密码到Wkhtmltopdf。
public virtual ActionResult PrintInvoice(int id) {
var invoice = db.Invoices.Single(i => i.InvoiceId == id);
var viewmodel = Mapper.Map<InvoiceViewModel>(invoice);
var reportName = string.Format(@"Invoice {0:I-000000}.pdf", invoice.InvoiceNo);
var switches = String.Format(@" --print-media-type --username {0} --password {1} ",
ConfigurationManager.AppSettings["PdfUserName"],
ConfigurationManager.AppSettings["PdfPassword"]);
ViewBag.isPDF = true;
return new ViewAsPdf("InvoiceDetails", viewmodel) {
FileName = reportName,
PageOrientation = Rotativa.Options.Orientation.Portrait,
PageSize = Rotativa.Options.Size.A4,
CustomSwitches = switches
};
}
Wkhtmltopdf.exe中运行的页面显示使用当前用户凭据运行,但Wkhtmltopdf.exe本身需要在服务器上执行的权限。
这在部署时适用于iis。在VS2012中的Cassini中,它对我来说没有凭据,但是在i20表达的vs2013中,我在找到像css和amp;等资源时仍然遇到麻烦。图像。
通过SSL运行相同的解决方案: Rotativa and wkhtmltopdf no CSS or images on iis6 over HTTPS, but fine on HTTP
答案 1 :(得分:0)
开启ASP.NET Impersonation和spawn wkhtmltopdf under the context of the impersonated user。
注意:启用ASP.NET模拟很可能会降低性能。