Asp.net MVC Razor视图模版动态渲染PDF,Razor模版生成静态Html

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位的插件。具体使用请看作者的说明://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="//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.运行效果

 

 

 

  6.项目代码

 代码托管://gitee.com/Jesai/JESAI.HtmlTemplate.Pdf