1.前言
上一篇文章我开源了*,Asp.net Core 3.1 Razor视图模版动态渲染PDF,然后,很多小伙伴有很多私信找我了。那么我下面就简单的给大家说一下,关于小伙伴问的这些问题。
- 我项目的电子签章部分代码可否开源?
答:我项目电子签章也是使用第三方的电子签章,电子签章并不是自己实现的,项目里面的电子签章代码无非也是对接第三方的接口。这部分代码开源出去也没有什么意义。我们是使用数字广东的方案,如果您也是使用该数字签章,可以私下沟通我看看能不能帮助您。
- 电子签章实现难不难,怎么实现自己的电子签章?
答:电子签章要实现,估计不是太难,按照我的理解,当然我没有具体深入研究(如果这里我有妄自菲薄的意思,请谅解,毕竟我能力有限,只是按照我的理解来分析),我个人觉得电子签章应该就是利用数字证书给PDF签名,然后加密保护文档,然后校验文档的真伪,就要考虑怎么验证这个文档没有被删改,是当初我们签章的这个文档,而且这个签名不能被伪造。个人觉得不是很复杂,但是,电子签章的法律有效性却不是这么简单的。按照国家法律规定,利用的签名平台应该有资质的,国家认可的第三方签章平台,也就是说,私人自己制作的签章,打起官司来,很难得到法律支持。
- 项目为什么CSS样式不起效?
答:你是否使用了外链的CSS样式,因为渲染Razor视图是在后台渲染,无法找到外链的文件路径,就使用不了外链的CSS样式,内嵌和内联CSS样式都没啥问题的。
- 用word或者excel模版他不香吗?为什么要搞个这个东西?
答:无非是多一个方案,具体你使用什么完全是你自己说了算,你觉得其他方案好就用,你觉得本方案能帮助你就用,不好就不用,我又不收你半毛线,还是想说的是,你其他的方案,能有用CSS那么容易做出来漂亮的表单效果吗?
- 图片支持吗?
答:图片要转Base64编码,不支持外连接图片。
- 前端预览PDF用什么插件?
答:我目前不用插件,新一代浏览器都支持PDF直接预览,直接就能渲染成PDF呈现,当然你也可以自己集成PDF.JS.我大概看了下,集成也很方便。我之所以不集成,是考虑到我的项目有可能使用IE低版本的情况,PDF.JS可能不支持。所以干脆直接把PDF流推送给浏览器,浏览器要是能预览,就直接呈现,不能预览就下载。
- 项目支持net Framework吗?为啥报错?
答:这个问题问的有点多,所以本文后续就以这个再说一下本*在net45下的使用。还是那句话,有不对的,欢迎您指正,觉得对你有用的就用,无用的就直接忽视,我又不收你半毛线。
- 可以用本项目生成静态Html吗?容易被搜索引擎抓取。
答:可以,后面演示
2.依赖项目
<PackageReference Include="TuesPechkin" Version="2.1.1" />
<PackageReference Include="TuesPechkin.Wkhtmltox.Win32" Version="0.12.2.1" />
<PackageReference Include="RazorEngine" Version="3.9.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.3" />
<PackageReference Include="Nito.AsyncEx" Version="4.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<Reference Include="Nito.AsyncEx.Concurrent" Version="4.0.1" />
<Reference Include="Nito.AsyncEx.Enlightenment" Version="4.0.1" />
3.核心代码
TuesPechkin插件,首先说一下这个TuesPechkin插件,他其实是利用TuesPechkin.Wkhtmltox进程来转换的。这个插件使用还是要小心的,使用不当可能有线程安全问题,会使得当前工作进程挂起。在IIS下面使用也要注意使用32位的插件。具体使用请看作者的说明:https://github.com/tuespetre/TuesPechkin/blob/develop/README.md
插件初始化代码:
private static readonly IConverter PdfConverter = new ThreadSafeConverter(new RemotingToolset<PdfToolset>( new Win32EmbeddedDeployment( new TempFolderDeployment())));
切记IIS和多线程一定要静态单例。使用
Win32EmbeddedDeployment
ThreadSafeConverter
这两个类。其他的可能让你进程挂起的成为可能。
还要用到远程工具集的PDF工具集。
Razor 转Html代码,主要有两种方式:
第一种使用RazorEngine来转换:这个主要是传递Razor模版进去,转换。
protected string RunCompileRazorTemplate(object model,string razorTemplateStr)
{
if(string.IsNullOrWhiteSpace(razorTemplateStr))
throw new ArgumentException("Razor模版不能为空");
var htmlString= Engine.Razor.RunCompile(razorTemplateStr, razorTemplateStr.GetHashCode().ToString(), null, model);
return htmlString;
}
第二种使用ViewEngine。这个主要是自动查找Asp.net MVC里面的View下面的Razor,目前我们项目就是使用这个。
var viewName = context.RouteData.Values["action"].ToString();
var result = ViewEngines.Engines.FindView(context, viewName, null);
IExportPdfByHtmlTemplate exportPdfByHtmlTemplate = new PdfByHtmlTemplateExporter ();
#endif
if (result.View == null)
throw new ArgumentException($"名称为:{viewName}的视图不存在,请检查!");
context.HttpContext.Response.ContentType = "application/pdf";
//context.HttpContext.Response.Headers.Add("Content-Disposition", "attachment; filename=test.pdf");
var html = "";
using (var stringWriter = new StringWriter())
{
#if !NET45
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = Value };
var viewContext = new ViewContext(context, result.View, viewDictionary, new TempDataDictionary(context.HttpContext, tempDataProvider), stringWriter, new HtmlHelperOptions());
await result.View.RenderAsync(viewContext);
#else
var viewDictionary = new ViewDataDictionary(new ModelStateDictionary()) { Model = Value };
var viewContext = new ViewContext(context, result.View, viewDictionary, context.Controller.TempData, stringWriter);
result.View.Render(viewContext, stringWriter);
result.ViewEngine.ReleaseView(context, result.View);
#endif
html = stringWriter.ToString();
}
推送PDF流给客户端,预览PDF主要代码
context.HttpContext.Response.ContentType = "application/pdf"; context.HttpContext.Response.AddHeader("Content-Length", buff.Length.ToString()); context.HttpContext.Response.AddHeader("Content-Disposition", "filename=电子签章PDF-"+DateTime.Now.ToString()+".pdf"); context.HttpContext.Response.BinaryWrite(buff); context.HttpContext.Response.Flush(); context.HttpContext.Response.Close(); context.HttpContext.Response.End();
这里要注意三个地方,不然一定会踩坑。
Content-Length要设置,不然谷歌浏览器可能无法下载预览的PDF。
Content-Disposition不能要attachment,否则可能直接下载不是预览。
ContentType 要设置"application/pdf"
Razor转静态Html
还有一部分人问我怎么利用本插件Razor模版动态生成静态Html,这样容易被百度爬虫录取。
其实这部分核心代码就是几句代码,非常简单。本项目直接用下面接口即可生成html字符转,自行保存就可以了。
public interface IHtmlByRazorTemplateExporter { Task<string> ExportHtmlByRazorTemplateAsync<T>(T data, string htmlTemplate) where T : class; string ExportHtmlByRazorTemplate<T>(T data, string htmlTemplate) where T : class; }
核心:
protected string RunCompileRazorTemplate(object model,string razorTemplateStr)
{
if(string.IsNullOrWhiteSpace(razorTemplateStr))
throw new ArgumentException("Razor模版不能为空");
var htmlString= Engine.Razor.RunCompile(razorTemplateStr, razorTemplateStr.GetHashCode().ToString(), null, model);
return htmlString;
}
4.使用方式
- 目前本项目已经打包成nuget,并上传,使用可以直接项目右键->管理NuGet程序包,查找,然后下载安装。
- 也可以使用命令安装。install-package JESAI.HtmlTemplate.Pdf.net45
- 或者直接fork本仓库自己打包,并根据自己情况修改使用。
自定打包可以修改项目目标框架。项目右键->属性->应用程序,目标框架,修改
如发现不能修改,可以,项目->右键->编辑项目文件
然后编译,就可以使用了。
具体使用
方式一:
方式二:
Razor视图模版代码:
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> </head> <body> <table border="1" style="width:800px;height:500px;"> <tr> <td>姓名</td> <td>@Model.Name</td> <td>性别</td> <td>@Model.Sex</td> </tr> <tr> <td>年龄</td> <td>@Model.Age</td> <td>班级</td> <td>@Model.Class</td> </tr> <tr> <td>住址</td> <td>@Model.Address</td> <td>电话</td> <td>@Model.Tel</td> </tr> <tr> <td clospan="2">住址</td> <td>@Model.Des</td> </tr> </table> </body> </html>
5.运行效果