一.需求描述
用户上传pdf文件后,需要将pdf逐页平铺展示给用户进行评论。
二.需求分析
需求服务端也能实现,但耗时较长。
三.核心方案
使用pdfjs-dist包,该插件是解决前端预览pdf文件的方案(像翻书一样,前后翻页展示),但我们只需要基于它提供的能力进行改造即可实现我们的需求。
四.详细示例
import * as pdfjsLib from 'pdfjs-dist';
import workerSrc from 'pdfjs-dist/build/pdf.worker.entry';
pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;
const fileReader = new FileReader();
fileReader.onload = function() {
const uint8 = new Uint8Array(this.result);
const loadingTask = pdfjsLib.getDocument(uint8);
loadPDFFile(loadingTask);
};
fileReader.readAsArrayBuffer(file); // file是拿到的上传的pdf文件
async function loadPDFFile(loadingTask) {
const pdf = await loadingTask.promise; // 拿到pdf对象
const pagesNum = pdf.numPages; // 拿到页数
// 循环获取每一页
for (const i = 0; i < pagesNum; i++) {
const page = await pdf.getPage(i);
const viewport = page.getViewport(1);
// 创建canvas渲染当前页
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport,
};
const renderTask = page.render(renderContext);
// 截止到这里,就实现了在canvas上渲染某一页(至于翻页,可以结合相关api,但这不是我们今天的需求)。
// 但我们的需求是拿到每一页平铺展示或者全部上传,见以下代码
renderTask.promise.then(async function() {
// 假如需要平铺展示,我们把canvas转为base64
const base64 = canvas.toDataURL('image/jpeg');
setImgs([...imgs, base64]); // react
// 上传,需要转blob格式(uploadBlob方法要求)
canvas.toBlob(async blob => {
const ext = blob?.type.split('/')[1] || 'png';
uploadBlob(blob, `${i}.${ext}`) // uploadBlob是上传方法,可自己封装ajax
})
})
}
}
五.性能优化
如果pdf页数比较多,需要考虑for循环中使用canvas渲染的浏览器性能消耗;以及上传存储云请求的并发控制。
请求的最大并行数量控制可参考我的另外一篇《# 前端ajax请求并发数量控制》juejin.cn/post/729341…
六.注意事项
1.因该包的最新版本使用较高的es版本编写(如#关键字),在我的项目中直接引用新版本会导致编译错误
该demo使用版本为"pdfjs-dist": "^2.0.943"