注意:html中需要在head元素中添加<meta charset="UTF-8">,以防生成的pdf中文乱码,另外,确保系统中有中文字体,否则也会出现乱码,如下:
5、 后端接口
仅保留关键代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = '授客'
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from backend.models import SprintTestReport
from django.utils import timezone
from django.http import FileResponse
from django.conf import settings
import pdfkit
import json
import base64
import uuid
import os
import logging
logger = logging.getLogger('mylogger')
class SprintTestreportPDFAPIView(APIView):
'''迭代测试报告pdf文件下载'''
@staticmethod
def convert_related_plans_to_html(self, related_plans):
'''转换报告相关联的测试计划数据格式为html格式数据,返回转换后的数据'''
result = ''
tr = '''<tr>
<td>
<div>{id}</div>
</td>
<td>
<div>{name}</div>
</td>
<td>
<div>{begin_time}</div>
</td>
<td>
<div>{start_time}</div>
</td>
<td>
<div>{end_time}</div>
</td>
<td>
<div>{finish_time}</div>
</td>
<td>
<div>{groups}</div>
</td>
<td>
<div>{environment}</div>
</td>
</tr>'''
for related_plan in related_plans:
result += tr.format(**related_plan)
return result
...略
def post(self, request, format=None):
'''下载pdf格式报告'''
result = {}
try:
data = request.data
report_id = data.get('report_id')
echart_base64_info_dict = data.get('echart_base64_info')
# 读取迭代测试报告html模板
report_html_str = '' # 存放html格式的迭代测试报告
current_dir, tail = os.path.split(os.path.abspath(__file__))
template_filepath = os.path.normpath(os.path.join(current_dir, 'sprint_test_report/sprint_test_report_template.html'))
with open(template_filepath, 'r', encoding='utf-8') as f:
for line in f:
report_html_str += line
# 读取报告数据
sprint_report = SprintTestReport.objects.filter(id=report_id)
if sprint_report.first():
try:
...略
report_data = sprint_report.values('title','introduction', 'related_plans', 'test_scope', 'individual_test_statistics', 'individual_dev_statistics', 'product_test_statistics', 'conclusion', 'suggestion', 'risk_analysis')[0]
# 替换测试计划
related_plans = json.loads(report_data['related_plans'])
related_plans = self.convert_related_plans_to_html(related_plans)
report_html_str = report_html_str.replace('${relatedPlans}', related_plans)
...略
# 生成echart图表图片
time_str = timezone.now().strftime('%Y%m%d')
uuid_time_str = str(uuid.uuid1()).replace('-', '') + time_str
file_name_dict = {}
for key, value in echart_base64_info_dict.items():
data_type, base64_data = value.split(',') # value 数据格式 data:image/png;base64,base64编码数据
file_suffix = '.' + data_type.split('/')[1].split(';')[0]
file_name = key + uuid_time_str + file_suffix
file_name_dict[key] = file_name
file_path = os.path.normpath(os.path.join(current_dir, 'sprint_test_report/%s' % file_name))
with open(file_path, 'wb') as f:
imgdata = base64.b64decode(base64_data)
f.write(imgdata)
# 替换 echart图表
for key in echart_base64_info_dict.keys():
# report_html_str = report_html_str.replace('${%s}' % key, '%s/sprint_test_report/%s' % (current_dir, file_name_dict[key])) # 注意,这里,迭代测试报告模板中的变量名称被设置为和key一样的值,所以可以这么操作
report_html_str = report_html_str.replace('${%s}' % key,os.path.normpath(os.path.join(current_dir, 'sprint_test_report/%s' % file_name_dict[key])))
# 生成pdf文档
time_str = timezone.now().strftime('%Y%m%d')
file_name = str(uuid.uuid1()).replace('-', '') + time_str + '.pdf'
config = pdfkit.configuration(wkhtmltopdf=settings.WKHTMLTOPDF)
file_dir = settings.MEDIA_ROOT + '/sprint/testreport'
options = {'dpi': 300, 'image-dpi':600, 'page-size':'A3', 'encoding':'UTF-8', 'page-width':'1903px'}
pdfkit.from_string(report_html_str, '%s/%s' % (file_dir, file_name), configuration=config, options=options)
file_absolute_path = '%s/%s' % (file_dir, file_name)
# 删除生成的图片文件
for key in echart_base64_info_dict.keys():
os.remove('%s/sprint_test_report/%s' % (current_dir, file_name_dict[key]))
# 返回数据给前端
if os.path.exists(file_absolute_path) and os.path.isfile(file_absolute_path):
file = open(file_absolute_path, 'rb')
file_response = FileResponse(file)
file_response['Content-Type']='application/octet-stream'
file_response['Content-Disposition']='attachment;filename={}.pdf'.format(report_data['title'] ) # 不知道为啥,前端获取不到请求头Content-Disposition
return file_response
else:
result['msg'] = '生成pdf报告失败'
result['success'] = False
return Response(result, status.HTTP_400_BAD_REQUEST)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(result, status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
result['msg'] = '生成迭代测试报告失败,报告不存在'
result['success'] = False
return Response(result, status.HTTP_400_BAD_REQUEST)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(result, status.HTTP_500_INTERNAL_SERVER_ERROR)
导出效果(部分截图)