ASP.NET MVC控制器生成HTML并通过WkHtmltoPdf转PDF的问题
在ASP.NET MVC控制器中生成HTML并转换为PDF的解决方案
问题分析
你当前代码里的View("Pdf", invoice).ToString()无法得到渲染后的HTML字符串——因为View()返回的是ViewResult对象,直接调用ToString()只会返回对象的类型名称,而非实际渲染出的HTML内容。另外,直接把HTML字符串作为命令行参数传给wkhtmltopdf还会遇到参数长度限制的问题,极易导致调用失败。
解决步骤
1. 实现视图转HTML字符串的方法
在控制器中注入IRazorViewEngine、ITempDataProvider和IHttpContextAccessor服务,编写方法将指定视图渲染为HTML字符串:
private async Task<string> RenderViewToStringAsync(string viewName, object model) { ViewData.Model = model; using var sw = new StringWriter(); var viewResult = _razorViewEngine.FindView(ControllerContext, viewName, false); if (viewResult.View == null) { throw new ArgumentNullException($"{viewName} 视图不存在"); } var viewContext = new ViewContext( ControllerContext, viewResult.View, ViewData, TempData, sw, new HtmlHelperOptions() ); await viewResult.View.RenderAsync(viewContext); return sw.ToString(); }
记得在控制器构造函数中注入所需服务:
private readonly IRazorViewEngine _razorViewEngine; private readonly ITempDataProvider _tempDataProvider; private readonly IHttpContextAccessor _httpContextAccessor; public YourController(IRazorViewEngine razorViewEngine, ITempDataProvider tempDataProvider, IHttpContextAccessor httpContextAccessor) { _razorViewEngine = razorViewEngine; _tempDataProvider = tempDataProvider; _httpContextAccessor = httpContextAccessor; }
2. 调用wkhtmltopdf转换HTML为PDF
得到HTML字符串后,不建议直接作为命令行参数传递,推荐以下两种更可靠的方式:
方式一:使用临时文件
public async Task<IActionResult> Pdf() { // 渲染视图为HTML字符串 var viewHtml = await RenderViewToStringAsync("Pdf", invoice); // 创建临时HTML文件 var tempHtmlPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.html"); await System.IO.File.WriteAllTextAsync(tempHtmlPath, viewHtml, Encoding.UTF8); // 定义PDF输出路径 var pdfPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.pdf"); // 配置wkhtmltopdf进程 var psi = new ProcessStartInfo() { FileName = @"C:\test\wkhtmltopdf.exe", Arguments = $"\"{tempHtmlPath}\" \"{pdfPath}\"", UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; using var process = Process.Start(psi); await process.WaitForExitAsync(); // 读取PDF内容并返回给客户端 var pdfBytes = await System.IO.File.ReadAllBytesAsync(pdfPath); // 清理临时文件 System.IO.File.Delete(tempHtmlPath); System.IO.File.Delete(pdfPath); return File(pdfBytes, "application/pdf", "invoice.pdf"); }
方式二:通过标准输入传递HTML
无需创建临时文件,直接将HTML内容通过标准输入传给wkhtmltopdf:
public async Task<IActionResult> Pdf() { var viewHtml = await RenderViewToStringAsync("Pdf", invoice); var pdfPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.pdf"); var psi = new ProcessStartInfo() { FileName = @"C:\test\wkhtmltopdf.exe", Arguments = "- " + $"\"{pdfPath}\"", // "-"表示从标准输入读取HTML UseShellExecute = false, CreateNoWindow = true, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true }; using var process = Process.Start(psi); await process.StandardInput.WriteAsync(viewHtml); process.StandardInput.Close(); // 关闭输入流,告知wkhtmltopdf开始处理 await process.WaitForExitAsync(); var pdfBytes = await System.IO.File.ReadAllBytesAsync(pdfPath); System.IO.File.Delete(pdfPath); return File(pdfBytes, "application/pdf", "invoice.pdf"); }
3. 注意事项
- 确保wkhtmltopdf的路径正确,且应用程序拥有该文件及临时目录的访问权限。
- 增加异常处理逻辑,比如捕获视图不存在、文件读写失败等情况。
- 生产环境中避免硬编码路径,可通过配置文件管理wkhtmltopdf的路径。
- 使用
Guid生成临时文件名,避免并发场景下的文件命名冲突。
内容的提问来源于stack exchange,提问作者Lemonade
相关产品推荐
相关产品推荐

