ASP.NET Core 3.1 MVC如何直接下载控制器返回的视图为PDF
在ASP.NET Core 3.1 MVC中实现视图直接下载为PDF
问题背景
点击下载按钮提交表单时,会调用以下控制器方法:
public async Task<IActionResult> InvigilatorDiaryPrint(InvigilatorDiaryDTO invigilatorDiaryDTO) { var status = this._invigilatorDiaryService.CreateInvigilatorDiaryEntry(invigilatorDiaryDTO); if (invigilatorDiaryDTO.StudentDetails.Count == 0 && status) { invigilatorDiaryDTO = await this._invigilatorDiaryService.GetStudentEntryDetails(invigilatorDiaryDTO); } return this.View("~/Views/InvigilatorDiary/InvigilatorDiaryPrint.cshtml", invigilatorDiaryDTO); }
当前该方法会直接展示InvigilatorDiaryPrint.cshtml视图,需求改为点击下载时直接将该视图内容生成PDF并触发下载,而非展示视图。
实现步骤
1. 安装视图转PDF依赖包
使用适用于.NET Core的DinkToPdf库,通过NuGet命令安装:
Install-Package DinkToPdf
同时根据系统架构(x86/x64)下载对应版本的wkhtmltopdf二进制文件,将其放在项目的wwwroot/wkhtmltopdf目录下(可自定义路径)。
2. 配置服务(Startup.cs)
在ConfigureServices中注册PDF转换服务:
using DinkToPdf; using DinkToPdf.Contracts; using System.Reflection; public void ConfigureServices(IServiceCollection services) { // 其他服务配置... var loadContext = new CustomAssemblyLoadContext(); // 替换为你存放wkhtmltox.dll的实际路径 loadContext.LoadUnmanagedLibrary(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "wkhtmltopdf", "x64", "wkhtmltox.dll")); services.AddSingleton<IConverter>(new SynchronizedConverter(new PdfTools())); } // 自定义加载上下文类,用于加载非托管库 public class CustomAssemblyLoadContext : AssemblyLoadContext { public IntPtr LoadUnmanagedLibrary(string absolutePath) { return LoadUnmanagedDll(absolutePath); } }
3. 修改控制器方法,实现视图转PDF下载
先添加一个辅助方法,用于将视图渲染为HTML字符串:
private async Task<string> RenderViewToStringAsync(string viewName, object model) { var httpContext = new DefaultHttpContext { RequestServices = HttpContext.RequestServices }; var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); using var sw = new StringWriter(); var viewResult = ViewEngines.Engines.FindView(actionContext, viewName, false); if (viewResult.View == null) { throw new ArgumentNullException($"{viewName} 视图不存在"); } var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = model }; var viewContext = new ViewContext( actionContext, viewResult.View, viewData, new TempDataDictionary(actionContext.HttpContext, TempDataProvider), sw, new HtmlHelperOptions() ); await viewResult.View.RenderAsync(viewContext); return sw.ToString(); }
然后修改原控制器方法,将视图转为PDF并返回下载文件:
private readonly IConverter _pdfConverter; // 通过构造函数注入PDF转换服务 public YourControllerName(IConverter pdfConverter) { _pdfConverter = pdfConverter; } public async Task<IActionResult> InvigilatorDiaryPrint(InvigilatorDiaryDTO invigilatorDiaryDTO) { var status = this._invigilatorDiaryService.CreateInvigilatorDiaryEntry(invigilatorDiaryDTO); if (invigilatorDiaryDTO.StudentDetails.Count == 0 && status) { invigilatorDiaryDTO = await this._invigilatorDiaryService.GetStudentEntryDetails(invigilatorDiaryDTO); } // 把目标视图渲染成HTML字符串 var htmlContent = await RenderViewToStringAsync("~/Views/InvigilatorDiary/InvigilatorDiaryPrint.cshtml", invigilatorDiaryDTO); // 配置PDF生成参数 var pdfConfig = new HtmlToPdfDocument() { GlobalSettings = new GlobalSettings() { ColorMode = ColorMode.Color, Orientation = Orientation.Portrait, PaperSize = PaperKind.A4, Margins = new MarginSettings { Top = 15, Bottom = 15, Left = 10, Right = 10 } }, Objects = { new ObjectSettings() { HtmlContent = htmlContent, WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet = Url.Content("~/css/pdf-style.css") }, HeaderSettings = { FontSize = 10, Right = "页码 [page]/[toPage]", Line = true }, FooterSettings = { FontSize = 10, Center = $"生成时间: {DateTime.Now:yyyy-MM-dd HH:mm}" } } } }; // 生成PDF字节数组 var pdfBytes = _pdfConverter.Convert(pdfConfig); // 返回PDF文件,触发浏览器下载 return File(pdfBytes, "application/pdf", $"监考日志_{DateTime.Now:yyyyMMddHHmmss}.pdf"); }
4. 注意事项
- 视图中引用的CSS、图片资源需使用绝对路径(可通过
Url.Content生成),否则PDF中可能无法正常加载样式和图片。 - 不同操作系统需要对应版本的
wkhtmltopdf二进制文件,部署时需注意匹配服务器架构。 - 复杂视图可能需要调整PDF参数(如页面方向、边距)以适配内容显示。
内容的提问来源于stack exchange,提问作者Athulya P D
相关产品推荐
相关产品推荐

