You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 22:30:20