使用pdf.js仅能读取2MB以内PDF,超量报错如何调整文件上限?
我之前也碰到过类似的坑,这个报错不是因为pdf.js有硬编码的2MB文件大小限制,而是postMessage在传输大量数据时的内存克隆开销触发了浏览器的内存上限。下面给你具体的修改思路和代码示例:
问题根源
你贴的MessageHandler.prototype.send方法里,每次调用postMessage都会把完整的data对象克隆一份传给Worker。当PDF文件过大时,单次克隆的数据量超过了浏览器能处理的内存阈值,就会抛出这个"Data cannot be cloned, out of memory"错误。
解决方案
我们可以从减少单次传输数据量和优化数据传输方式两个方向来修改:
1. 分块传输大二进制数据
如果你的data是PDF的二进制Uint8Array,把它拆成小块分批发送,接收端再拼接起来。修改send方法如下:
send: function messageHandlerSend(actionName, data) { // 针对大二进制数据做分块处理,这里设置64KB为分块阈值 if (data instanceof Uint8Array && data.length > 65536) { const chunkSize = 65536; const totalChunks = Math.ceil(data.length / chunkSize); for (let i = 0; i < totalChunks; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, data.length); const chunk = data.slice(start, end); this.comObj.postMessage({ action: actionName, data: chunk, chunkIndex: i, totalChunks: totalChunks, isChunked: true // 标记这是分块数据 }); } } else { // 小数据保持原有逻辑发送 this.comObj.postMessage({ action: actionName, data: data }); } }
同时,你需要在接收消息的Worker端(或者对应的处理函数)添加拼接逻辑:
- 维护一个缓存对象,存储已收到的分块
- 当所有分块都接收完成后,把它们合并成完整的Uint8Array,再执行原来的PDF解析逻辑
2. 使用Transferable Objects避免数据克隆
对于可转移的对象(比如Uint8Array的buffer),可以通过postMessage的第二个参数把数据转移给Worker,而不是克隆,这样能大幅节省内存:
send: function messageHandlerSend(actionName, data) { if (data instanceof Uint8Array) { // 转移ArrayBuffer,避免克隆 this.comObj.postMessage({ action: actionName, data: data }, [data.buffer]); } else { this.comObj.postMessage({ action: actionName, data: data }); } }
⚠️ 注意:转移后原上下文(主线程)就不能再访问这个data.buffer了,所以要确保主线程后续不再需要这份数据。
3. 优化Worker内存回收
另外,你可以检查pdf.js中Worker的页面处理逻辑,确保渲染完页面后及时清理不再需要的临时数据,避免内存堆积。比如在页面渲染完成后,主动释放相关的二进制缓存或DOM对象。
最后提醒
修改完pdf.js的源码后,如果是用源码构建的版本,需要重新编译打包;如果是直接用预构建的js文件,替换修改后的文件即可。
内容的提问来源于stack exchange,提问作者Aditya Batra

