vue +pdfkit+wkhtmltopdf导出pdf

一、前言

项目背景: vue+Django+pdfkit+wkhtmltopdf
思路: 后端建立一个txt文件格式的html模板(具体内容请看下文),然后将需要的css放在模板的head中。前端通过设置页面id,抓取id标签下面的所有内容,传到后台,和模板结合。最后利用pdfkit库和wkhtmltopdf,将html页面导出成pdf

二、问题

  1. 因为vue项目是一个单页应用,所有的css会被解析放到html页面的head标签中,如果简单的将所有css复制到模板文件中,会有造成css很大的冗余。并且还使用了element组件开发,增加了筛选css的难度。
  2. vue项目开发有个特点,放在<style scoped></style>标签中的私有css在被webpack打包时,class对应的css名称会带上唯一的hash值,导致模板不可以重复使用。
  3. 使用element开发,有一些组件在导出成pdf时,会消失,具体原因不明。
  4. 图片问题,如果使用比较大的图片,同样不能导出,因为模板中无法加载图片链接。
  5. 导出pdf清晰度问题,这个和css有很大关系。

三、解决办法

  1. 针对问题1、2、3,最好的策略是使用传统html和css,单独开发一个导出页面,这样就可以省去不可控的外部组件,而且css可以统一放在一个单独的公共scss文件中,这样可以去除打包时产生的hash值问题,同样可以抵消css冗余问题。
  2. 如果项目进度比较急 ,可以使用element的一些简单组件,比如button和布局等一些简单的不需要js逻辑处理的组件,目前探索的也不是很多,可以自行测试。
  3. 针对问题4,可以使用小于webpack规定大小的图片,这样图片可以通过url-loader压缩成base64编码,直接显示在html页面中,无需通过路径加载图片。
  4. 针对问题5,css样式不要使用阴影,很影响清晰度。

四、其他思路

1、pdfkit有一个导出是通过html的链接来导出pdf,亲测百度链接有效。但是因为项目时间紧迫,没来的急在生产进行这方面的测试,以后有条件可以去试试。
2、前端直接生成图片,再导出成pdf,是一个可行的思路。但是有个问题就是前端生成图片清晰度不够,目前还没有找到比较好的方法,生成清晰的图片。

五、技术问题和解决办法

导出table,在pdf换页时表头有重复问题,解决办法是设置table标签的样式,办法如下:

 thead {
            display: table-row-group;// 使用thead默认每页都显示表头
        }

六、具体实现部分

后端

模板export_file.txt
在这里插入图片描述

EXPORT_FILE = os.path.join(PROJECT_ROOT, 'fixtures/export_file.txt')
@csrf_exempt
def export_pdf(request):
    try:
        # params = json.loads(request.body)
        # report_id = params.get('report_id', '')
        # path = params.get('path', '')
        content = request.POST.get("content")
        html_string = format_export_string(content)
        report_id = request.GET["report_id"]
        check_report = CheckReport.objects.get(id=report_id)
        file_name = check_report.task_name + "-" + check_report.created_time + ".pdf"
        options = {
            'page-size': 'A4',
            'encoding': "UTF-8",
            "javascript-delay": "5000",
            "margin-top": "0",
            "margin-bottom": "0",
            "margin-left": "0",
            "margin-right": "0",
            'quiet': "",
        }
        if settings.DEBUG:
            configuration = pdfkit.configuration(wkhtmltopdf=r'E:\wkhtmltopdf\bin\wkhtmltopdf.exe')
        else:
            configuration = pdfkit.configuration(wkhtmltopdf='/usr/bin/wkhtmltopdf')
        pdf_path = False
        pdf_file = pdfkit.from_string(html_string, pdf_path, options=options, configuration=configuration)
        # Pdf.objects.create(report_id=report_id, name=file_name, status=True, operator=request.user.username,
        #                    value=pdf_file)
        return download_file(pdf_file, file_name)
    except Exception as e:
        return JsonResponse({"result": False, "message": str(e)})


def format_export_string(content):
    export_file = open(EXPORT_FILE, encoding='utf-8')
    file_content = export_file.read()
    export_file.close()
    html_content = content.replace("&amp;", "&").replace("&gt;", ">") \
        .replace('&quot;', '"') \
        .replace("&#039;", r"'") \
        .replace("&lt;", "<") \
        .replace("&nbsp;", " ") \
        .replace("\n", '') \
        .replace("\r", "").strip()
    return str(file_content).replace('{body_content}', html_content)


def download_file(file_buffer, file_name):
    response = HttpResponse(file_buffer, content_type='APPLICATION/OCTET-STREAM')
    response['Content-Disposition'] = 'attachment; filename=' + file_name
    response['Content-Length'] = len(file_buffer)
    return response
前端

在vue项目中,css样式放到公共scss中,不要放在vue文件的style中

downLoadPdf() {
      const VueEnv = process.env.NODE_ENV
      const ApiUrl = VueEnv === 'production' ? window.siteUrl : 'http://127.0.0.1:8083/'
      const eleForm = document.createElement('form')
      eleForm.id = 'eleForm'
      eleForm.method = 'post'
      eleForm.action = ApiUrl + 'check/export_detail_pdf/?report_id=' + this.reportId + '&ip=' + this.ipAddress
      eleForm.target = '导出报告'
      const eleInput = document.createElement('input')
      eleInput.type = 'hidden'
      eleInput.name = 'content'
      eleInput.value = $('#export-pdf').html()
      eleForm.appendChild(eleInput)
      eleForm.addEventListener('onsubmit', function() {
        this.$message.success('导出报告')
      })
      document.body.appendChild(eleForm)
      eleForm.submit()
      document.body.removeChild(eleForm)
    },

七、Django下载文件,文件名为中文时,出错问题

解决办法:

response['Content-Disposition'] = "attachment; filename*=utf-8''{}".format(escape_uri_path(file_name))
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值