JavaScript前端提取PDF图片的完整指南
本文详解如何利用JavaScript和PDF.js在前端直接提取PDF文件中的图片。通过结合Web Worker、OffscreenCanvas和ImageBitmap技术,实现异步非阻塞的图片渲染与转换,有效避免主线程卡顿。文章对比了前后端处理的优劣,提供了从文件上传到图片生成的完整代码示例及性能优化建议。
提取PDF图片,指的是从文档的每一页中单独抽取其中的多张图片,而不是将整页内容合并转换为一张大图。
效果展示

技术实现详解
本项目采用PDF.js库来解析和渲染PDF文件。该库在GitHub上拥有43.9kb的Star数,属于非常成熟的开源方案。
一、引入pdf.js
在项目中需要同时引入pdf.js核心库和WebWorker。WebWorker的作用是在浏览器的子线程中解析图片等资源,从而避免阻塞主页面的UI渲染和用户交互。引入代码如下:
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.10.111/pdf.js"></script>
<script>
// pdf.js 从性能考虑,使用了 WebWorker, 在浏览器子线程解析图片等资源,不阻塞页面UI和交互。
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.10.111/build/pdf.worker.js';
</script>
二、上传PDF文件并监听事件
在用户上传文件时,使用FileReader异步读取文件对象。读取完成后,将其转换为Uint8Array格式,并传入pdfjsLib以获取PDF加载任务。
具体实现代码如下:
<input type="file" id="fileInput" name="avatar" title ="选择 pdf 文件"/>
<script>
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener(
"change",
() => {
const file = fileInput.files[0];
console.log('fileInput file: ', file);
// 上传文件时,使用 FileReader 对象读取
var fileReader = new FileReader();
fileReader.onload = function() {
// 将文件对象转化为 Uint8Array
var typedarray = new Uint8Array(this.result);
// 返回 PDF 加载任务 PDFDocumentLoadingTask
const loadingTask = pdfjsLib.getDocument(typedarray);
// 开始加载 PDF,这里封装了一个函数
loadPDFFile(loadingTask);
};
fileReader.readAsArrayBuffer(file);
},
false
);
</script>
三、获取并遍历PDF页数
async function loadPDFFile(loadingTask) {
// PDF 加载任务
const pdf = await loadingTask.promise;
// 获取 PDF 页数
const numPages = pdf.numPages;
for (let curPage = 1; curPage <= numPages; curPage++) {
// 返回当前页
console.log('loadingServerFile curPage: ', curPage);
const page = await pdf.getPage(curPage);
const scale = 1.5;
// 获取渲染视角尺寸
const viewport = page.getViewport({ scale });
// Support HiDPI-screens.
const outputScale = window.devicePixelRatio || 1;
// 传入离屏 Canvas
const canvas = new OffscreenCanvas(200, 200);
// 获取 Canvas 上下文
const context = canvas.getContext("2d");
// 转换尺寸
const transform = outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null;
const renderContext = {
canvasContext: context,
transform,
viewport,
};
// 调用 page.render 触发渲染
const renderTask = page.render(renderContext);
// 等待 renderTask 渲染任务
// await delay(500); // 如果渲染过快,可能导致 CPU 飙升、电脑可用内存不够,可以加延迟减慢速度
await renderTask.promise;
}
}
在代码实现中,使用了OffscreenCanvas。这是一个可以脱离屏幕直接渲染的Canvas对象,既可以在主线程使用,也可以在Web Worker子线程中使用,旨在优化性能。虽然提取PDF图片不需要将结果渲染到页面可见区域,但仍需要执行渲染任务。
需要注意的是,如果渲染速度过快,可能会导致CPU占用率飙升或电脑可用内存不足。可以通过添加setTimeout延迟来降低处理速度,以平衡性能。
四、提取每页PDF中的图片
在此步骤中,我们获取了由pdfjs提取出的PDF图片,其格式为ImageBitmap。ImageBitmap是对Canvas上位图数据的引用,数据存储在GPU中。它可以在Web Worker中生成,从而避免影响主线程的UI绘制,也不会阻塞用户对页面的交互响应。
相关代码实现如下:
async function loadPDFFile(loadingTask) {
// ...接上一步
const renderTask = page.render(renderContext);
// 等待当前页渲染任务执行
await renderTask.promise;
// 获取操作列表
const ops = await page.getOperatorList();
// 提取图片
const imageNames = ops.fnArray.reduce((acc, curr, i) => {
if ([pdfjsLib.OPS.paintImageXObject, pdfjsLib.OPS.paintJpegXObject].includes(curr)) {
acc.push(ops.argsArray[i][0]);
}
return acc;
}, []);
for (const imageName of imageNames) {
console.log('imageName: ', imageName);
page.objs.get(imageName, (image) => {
// console.log('image: ', image);
(async function() {
const bmp = image.bitmap;
// create a canvas
console.log('bmp: ', bmp);
})();
});
}
}
五、将ImageBitmap转换为Img标签
最后一步,也是关键的一步!─=≡Σ(((つ•̀ω•́)つ
在上一步中,我们获取了Canvas绘图引用的ImageBitmap对象,现在需要将其转换为浏览器中标准的Img图片元素。
具体转换过程为:先将ImageBitmap渲染到Canvas上,然后调用canvas.convertToBlob()方法即可得到Blob对象。
async function loadPDFFile(loadingTask) {
// ...接上一步,拿到了每页 PDF 的 image.bitmap 对象
for (const imageName of imageNames) {
console.log('imageName: ', imageName);
page.objs.get(imageName, (image) => {
// console.log('image: ', image);
(async function() {
const bmp = image.bitmap;
console.log('bmp: ', bmp);
// OffscreenCanvas
const resizeScale = 1/4; // 这个可以控制转换后的图片大小
const width = bmp.width * resizeScale;
const height = bmp.height * resizeScale;
const canvas = new OffscreenCanvas(width, height);
// 获取 canvas bitmaprenderer 上下文
const ctx = canvas.getContext('bitmaprenderer');
// 把 ImageBitmap 渲染到 OffscreenCanvas
ctx.transferFromImageBitmap(bmp);
// 把 canvas 画布转化为 Blob 对象
// https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas/convertToBlob
const blob = await canvas.convertToBlob();
console.log('blob: ', blob); // blob
// 最后使用 Blob 作为 URL.createObjectURL 的参数,渲染出 img 图片
// 如果不需要渲染,则可以讲 Blob 数据上传到云存储
const img = document.body.appendChild(new Image());
img.width = width;
img.height = height;
img.src = URL.createObjectURL(blob);
})();
});
}
}
OffscreenCanvas可以放置在Web Worker中创建,这样就不会阻塞主线程的UI绘制和用户交互响应。
使用bitmaprenderer.transferFromImageBitmap()可以实现不拷贝数据、仅传递内存地址的方式,将ImageBitmap高效地传递到Canvas上。
背景(缘起)
前段时间,项目需求中涉及从PDF中提取图片,用于评论展示和审核流程。
由于项目时间紧迫、任务繁重,最初由后端实现了将一页PDF转换为一张图片的功能。
后端处理方案的优劣分析如下:
- 优点:用户体验较好,不会占用用户本地电脑的性能资源。
- 劣:加大服务器压力
- 缺点:占用大量CPU资源。在我使用的Mac笔记本
MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)上进行解析时,观察到CPU使用率每次都飙升至100%以上。 - 缺点:占用大量内存。解析后的图片在上传至CDN之前,一直驻留在服务器内存中未被释放。
- 缺点:需要将PDF文件先上传到服务器,再由后端进行处理,增加了网络传输和服务器负载。
- 缺点:占用大量CPU资源。在我使用的Mac笔记本
前端处理方案的优劣分析如下:
- 优点:不占用服务器资源,所有提取操作均在浏览器端完成。
- 缺点:可能会引起用户电脑出现卡顿现象,取决于设备性能。
以上就是JavaScript提取PDF图片的方法详解的详细内容,更多关于JavaScript提取PDF图片的资料请关注本站其它相关文章!
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
