文章目录
前言
参考文章:实测可行、但是需要修改
前端vue的JsPDF html2canvas 生成pdf并以文件流形式上传到后端
项目背景:vue2
框架:若依
需求:把页面上某一块内容(或者整个页面),转为pdf文件上传到后端、或者下载到本地
提示:以下是本篇文章正文内容,下面案例可供参考
一、安装插件
npm i html2canvas --save
npm i jspdf --save
二、使用步骤
1. utils/htmlToPdf.js
代码如下(示例):
需要修改id为你的页面上内容的id
我这里是#printer
import html2Canvas from 'html2canvas';
import JsPDF from 'jspdf';
export default {
install(Vue, options) {
/**
*
* @param {*} reportName 下载时候的标题
* @param {*} isDownload 是否下载默认为下载,传false不下载
*/
Vue.prototype.getPdf = function (reportName, isDownload = true) {
// var target = document.getElementsByClassName("right-aside")[0];
// target.style.background = "#FFFFFF";
return new Promise((resolve, reject) => {
var title = reportName;
html2Canvas(document.querySelector('#printer'), {
allowTaint: true,
}).then((canvas) => {
let contentWidth = canvas.width;
let contentHeight = canvas.height;
//一页pdf显示html页面生成的canvas高度;
let pageHeight = (contentWidth / 592.28) * 841.89;
//未生成pdf的html页面高度
let leftHeight = contentHeight;
//页面偏移
let position = 0;
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
let imgWidth = 595.28;
let imgHeight = (592.28 / contentWidth) * contentHeight;
let pageData = canvas.toDataURL('image/jpeg', 1.0);
let PDF = new JsPDF('', 'pt', 'a4');
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= 841.89;
//避免添加空白页
if (leftHeight > 0) {
PDF.addPage();
}
}
}
if (isDownload) {
PDF.save(title + '.pdf');
}
// 删除本地存储的base64字段
var pdfData = PDF.output('datauristring'); //获取base64Pdf
resolve(pdfData);
});
});
};
},
};
2. main.js
代码如下(示例):
import htmlToPdf from '@/utils/htmlToPdf'; // 下载+上传
Vue.use(htmlToPdf);
3. utils/base64.js
这个是base64转blob,不是base64加密
// 转换为 Blob 对象的方法 (可复用)
export function base64ToBlob(base64Data) {
const parts = base64Data.split(";base64,");
const contentType = parts[0].split(":")[1];
const raw = window.atob(parts[1]);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
}
// 从 base64 数据中获取文件名和 mime 类型的方法 (可复用)
export function getFilenameAndMimetypeFromBase64(base64Data) {
const fileInfo = base64Data.split(';base64,')[0].substring(5).split(':');
const mimeType = fileInfo[0];
const filename = fileInfo[1];
return [filename, mimeType];
}
4. 上传方法
只是api方法示例,基本上vue框架,都会封装request.js,如果看不懂,可以翻原文
import request from '@/utils/request';
// 上传签名图片 || 上传手写意见图片
export function uploadSign(data) {
return request({
url: '/flow/common/upload',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: data,
});
}
5. vue页面内容
我这里需要可复用性,所以关键性都在utils中,
5.1 引入方法
import { uploadSign } from '@/api/flow/common.js';
import { base64ToBlob, getFilenameAndMimetypeFromBase64 } from '@/utils/base64.js';
// getFilenameAndMimetypeFromBase64 可以不用
// 因为我指定了上传文件是pdf,所以不需要去查找是什么类型
5.2 页面内容div
需要id
和utils/htmlToPdf.js
中的对应
<el-button type="primary" icon="el-icon-printer" @click="toGetPdf(0)">只下载</el-button>
<el-button type="warning" icon="el-icon-printer" @click="toGetPdf(1, 0)">只上传</el-button>
<div id="printer" class="table">
...
</div>
5.3 js方法
// 保存方法
toGetPdf(val = false, download = true) {
/**
* val 决定走不走上传接口,默认为不上传给后端
* download 默认是下载
* /
/* */
this.$nextTick(() => {
setTimeout(() => {
window.scrollTo(0, 0); //这行代码很重要,它让页面的滚动条跳到了最上方如果点击打印按钮的时候,
// 滚动条没有在最上方,打印内容会是不完整的,体验也会差
let title = '个人报告';
this.getPdf(title, download) //download:false为不下载,这里调用了刚刚引用的全局函数,
// .then得到的值是base64位的pdf文件
.then((res) => {
if (val) {
console.log('准备上传');
this.UploadPdf(res);
} else {
console.log('不上传');
}
});
}, 1000);
});
},
//上传pdf接口
UploadPdf(res) {
//res拿到base64的pdf
let pdfBase64Str = res;
let filename = '测试.pdf';
// 将 base64 编码的文件数据转为 Blob 对象
const blob = base64ToBlob(pdfBase64Str);
// 获取文件名和 mime 类型
// const [filename, mimeType] = getFilenameAndMimetypeFromBase64(res);
// 将 Blob 对象转换为 File 对象
const file = new File([blob], filename, { type: '.pdf' }); // type类型对应的是mimeType
// 上传文件
const formData = new FormData();
formData.append('file', file);
formData.append('name', filename);
//该uploadMy为接口,直接以formdata格式传给后台
uploadSign(formData) // 改为你自己的上传接口
.then((res) => {
console.log('上传pdf接口', res);
})
.catch((err) => {
console.log('上传pdf接口', err);
});
},
注释事项
- 把htmlToPdf.js中的打印对应id,改成你的页面内容的。你也可以改写为变量传入
- 上传方法是request.js,一定要加headers类型
‘Content-Type’: ‘multipart/form-data’, - 点击方法传参,两个都是boolean类型,用1和0是因为隐式转换
- formData.append(‘name’, filename)是把名称传给后端
总结
如有不懂,请看原文。
我这里是基于原文实现,并且修改部分内容。
js指定页面内容另存为pdf文件
前端vue把页面转为file文件流上传到后端