记一次被wkhtmltopdf坑的经历

	今天不讲代码,给大家讲讲我在工作中的一次填坑的难忘经历。

在之前的一个项目中有一处需要生成pdf文件,向公司大佬请教如何生成pdf,大佬推荐了2种方式:

  • Itextsharp
  • wkhtmltopdf
    因为pdf的内容中包含3个行数不固定的表格(需要后端获取数据),和个数不固定的图片(同样大小的一块儿区域如果一张照片就填充满,2张就对半分…)。这样一来如果直接操作pdf,通过代码向pdf中写入内容的话,需要做的逻辑 处理实在太多,而且后期如果pdf内容需要调整,那么修改后端代码将是一间非常痛苦的事情。
    而大佬推荐的wkhtmltopdf 具备可以直接把一个url对应的页面转成pdf的功能。为此大喜。从而确定了如下的实现思路。
wkhtmltopdf访问这个url转成pdf
pdf内容做成html
pdf保存到服务端

这样就完美的解决了:
1.pdf内容与生成pdf文件的分离
2.pdf内容需要修改的时候,我们只需要修改对应html页的实现即可(相信这点一定会让程序员惊喜)
3.后期如果导出pdf的内容不正确(数据不对,位置错误,图片不在…)的现象我们可以第一步通过直接浏览器打开这个html页面查看是否本身数据就是缺失的。快速定位是内容出错还是导出出错。
接下来就是撸起袖子加油干了。
编码之路:可以说的上是一路绿灯,网上教程很多,轻轻松松实现根据url就生成pdf
测试之路:公司内部测试以及客户测试环境测试仍然一路绿灯。
产品上线:各种搞怪问题都来了。

坑一:功能直接无法使用

因为导出PDF的功能只在月底做对公的文档使用,所以在用户使用前期并未使用到,问题也就没能及时的暴露出来。
月底客户使用的时候结果发现导出的PDF不能打开,提示文件已受损。
问题出现开始各种排查:

  • 怀疑html内容有问题:打开html页面发现一切正常。
  • url转为pdf文件代码有问题:单独将这一块儿的代码撸出来本地执行发现很完美不报任何错误。
  • 进入傻眼模式,html内容没问题,代码也没问题,那是哪儿的问题?百思不得其解,终于开始考虑是不是服务器的问题。
  • url转pdf测试:用控制台应用程序写了将url转pdf的代码,放到服务器上执行,执行即报错:msvcp140.dll不存在。
  • 发现问题:正式环境服务器没有安装过vs,而本地开发,用户测试服务器都是安装过vs的所有对应需要的一些文件都齐全所以一切正常。
  • 解决问题:用户不允许我们在服务器上安装vs,所有只能缺啥补啥,缺少dll我就提供对应的dll就是。msvcp140.dll确实解决方案
  • 运气使然发现了msvcp140.dll的缺失。因为在我的程序使用过程中,缺少这个dll,exe调用失败,整个过程竟然没有抛出任何错误,对就是没有抛出任何错误,不是我没处理。要不是用控制应用测试了一下,在控制台暴露出来了,我都发现不了这个问题。

2020-7-24 补充: 通过官网的了解,这个坑的出现本质上是因为,wkhtmltopdf是使用Qt WebKit渲染引擎将HTML渲染为PDF,QT是C++图形用户界面应用程序开发框架,即使用wkhtmltopdf需要C++环境的支持。

坑二:ajax请求动态加载的表格数据和图片数据在pdf中没有展示,其余数据正常。

问题发现及过程:
坑一解决后进行过导出pdf测试,发现一切正常(只是当时需要导出的数据没有图片),此后相安无事了一段时间。
但是2个月后,客户突然反馈,说导出的pdf中明细行的数据和图片没有。
(导出的数据应该包含这些内容的,而且缺失的数据刚好是我ajax请求之后加载的数据)

  • 确认是否html内容是否本身缺失这些内容:单独访问html页面发现数据是加载了的。
  • 是不是ajax请求处理过长导致页面未完全加载就导出pdf了:通过对html页监测ajax请求,发现用时很短,只有几十毫秒。所以理论上不存在请求过长
  • 尽管页面监测ajax请求时间不长但是为了避免麻烦我还是将异步请求修改为同步,同时将–javascript-delay–参数设置为6000毫秒,等待js执行6秒。
  • 仍然还是不行,为了彻底排除是否是js加载导致的,我将js延时等待设置到了20秒,发现仍然不行。于是彻底放弃是js导致的内容丢失的问题
  • 终于还是走到了排查服务器的地步:
  • 第一步:确认是否代码有问题:我仍然用控制台应用程序,使用同样的url转pdf的后端代码,转相同的url(用户可以外网访问,所以url直接是外网的url)。本地转一起pdf发现正常,明细表格和图片都加载出来了。
  • 第二步:是否又报错但是系统未抓取,控制应用程序放到客户正式环境服务器上执行发现导出内容丢失。
  • 这个时候我初步怀疑是否是正式环境未安装vs导致有一样其它需要的环境导致了本地环境可以,正式服务器不可以。(心里盘算着要是vs的缘故,直接说服客户安装VS吧)
  • 于是我用了我用了和客户一样的windows server2012的服务器,安装了vs2015之后,在我的服务器上执行一般处理程序,发现导出数据仍然丢失,vs未安装导致的猜测确认失败。
  • 至此我已经毫无办法,安装vs是我最后救命稻草,现在也没了。
  • 通过求教学长,学长介绍了另一个方法通过jspdf直接在前端将html转pdf文件,于是只好在客户要导出的时候先将html页面展示出来,然后再让客户执行导出操作。无奈的是测试的时候本地用的谷歌浏览器,但是客户只允许使用ie浏览器,jspdf在ie浏览器上不兼容,花费8个小时解决兼容问题未能搞定,最后放弃。
  • 幸好在CSDN中看到一篇博文将自己被htmltopdf坑的经历,发现与我类似,最终通过他介绍的方案解决了我的问题,解决思路如下:
    在这里插入图片描述原文链接
    核心将之前的直接将url转为pdf,中间增加一步,先将url页面打开加载,加载完成之后,将加载后的页面转存为一个静态页面,然后将这个静态页面转为pdf。
    代码如下:
    获取动态资源
document.getElementById("downloadPdf").onclick = function () {
    
    
            
            var body = document.getElementById("iftest").contentDocument.body;
            var head = document.getElementById("iftest").contentDocument.head;
            //拼接成html
            var htmlStr = "<html>" + head.innerHTML + body.innerHTML + "</html>";
            //传递到后端
            $.ajax({
    
    
                type: "post",
                url: "../../../Report/Service/MaintainReportData.ashx",
                async: false,//同步
                data: {
    
    
                    Method: 'ExceclPDF2',
                    HtmlStr: encodeURI(htmlStr),//注意这里必须要进行转码,负责Ajax会报错
                },
                success: function (result) {
    
    
                    //获取返回参数
                    var data = eval('(' + result + ')');
                    if (data.result == 0) {
    
    
                        //(文件名,文件路径)
                        var filepath = data.FilePath;
                        var filename = data.FileName;
                        if (filepath != undefined && filepath != "") {
    
    
                            //请求下载pdf的url
                            window.location.href = "../../../Report/Service/MaintainReportData.ashx?Method=DownLoadFile&FileName=" + "&FilePath=" + filepath;
                        }
                    }
                    else {
    
    
                        alert(data.msg);
                    }
                }
            });

        }

中间服务层转为html

 string ssss = Convert.ToString(context.Request.Params["HtmlStr"]);
 string htmlStr = HttpUtility.UrlDecode(ssss);
 string htmlfileName = "维修报告书" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".html";
 htmlSavPath = context.Request.MapPath("~/BXApp/IRep/ExportPDF/MaintainReport/HtmlFile/" + htmlfileName);
 //生成html静态文件
 CreateHtml(htmlStr, htmlSavPath);

 private void CreateHtml(string htmlStr, string saveHtmlFilePath)
 {
    
    
        using (FileStream file = File.Create(saveHtmlFilePath))
        {
    
    
            Encoding myEncoding = Encoding.GetEncoding("utf-8");
            byte[] myByte = myEncoding.GetBytes(htmlStr);
            file.Write(myByte, 0, myByte.Length);
        }
 }

最后,非常在这里非常感谢学长和博主对我的帮助,搬砖的道路上感谢有你们。

2020-7-24补充 windows server 2012 上执行数据丢失,而win执行不丢失根本原因可能在于,QT不支持win2012的环境,支持win2008,win7,win10 等。

猜你喜欢

转载自blog.csdn.net/qq_39541254/article/details/107541497