RemixJs+TypeScript环境下pdfjs-dist Worker导入及配置问题求助
在Remix.js + TypeScript中配置pdfjs-dist并读取PDF文本
问题分析
你遇到的核心问题有两个:
GlobalWorkerOptions配置错误,直接导入worker模块而非指定可访问的脚本URL- worker文件无默认导出导致导入报错
下面提供两种可行的解决方案,均适配Remix+TS环境。
前置准备
确保依赖版本匹配:
- 安装核心库:
npm install pdfjs-dist - 安装对应类型声明:
npm install -D @types/pdfjs-dist
注意:
@types/pdfjs-dist版本需与pdfjs-dist主版本一致,避免类型不兼容
方案一:使用workerPort(推荐,ES模块友好)
利用Remix/Vite的?worker后缀语法,直接将worker作为模块导入并实例化,无需全局配置。
'use client'; // 必须添加,因为涉及浏览器API(FileReader、Worker) import { getDocument } from 'pdfjs-dist'; import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker'; import { useState } from 'react'; export default function DocumentTranslation() { const [characterCount, setCharacterCount] = useState(0); const [downloadFileName, setDownloadFileName] = useState(''); const handleFileSelect = (file: File) => { if (file.type !== 'application/pdf') return; const reader = new FileReader(); reader.onload = async (e) => { try { const typedArray = new Uint8Array(e.target.result as ArrayBuffer); // 直接传入worker实例到getDocument const pdfDoc = await getDocument({ data: typedArray, workerPort: new pdfjsWorker() }).promise; let textContent = ''; for (let i = 1; i <= pdfDoc.numPages; i++) { const page = await pdfDoc.getPage(i); const textData = await page.getTextContent(); // 类型断言确保正确访问str属性 textContent += textData.items.map(item => (item as { str: string }).str).join(' '); } setCharacterCount(textContent.length); const baseName = file.name.split('.').slice(0, -1).join('.'); setDownloadFileName(`${baseName}_processed.pdf`); } catch (err) { console.error('读取PDF失败:', err); } }; reader.readAsArrayBuffer(file); }; // ...组件渲染逻辑 return ( <div> <input type="file" accept="application/pdf" onChange={(e) => e.target.files && handleFileSelect(e.target.files[0])} /> {characterCount > 0 && <p>字符数:{characterCount}</p>} {downloadFileName && <p>处理后文件名:{downloadFileName}</p>} </div> ); }
方案二:配置GlobalWorkerOptions.workerSrc
通过指定worker脚本的URL(本地或CDN)来全局配置,适合需要共享worker配置的场景。
步骤1:复制worker文件到public目录
将node_modules/pdfjs-dist/build/pdf.worker.min.mjs复制到Remix项目的public目录下,这样浏览器可以直接访问到该脚本。
步骤2:代码实现
'use client'; import { getDocument, GlobalWorkerOptions } from 'pdfjs-dist'; import { useState } from 'react'; // 仅在浏览器环境中配置worker if (typeof window !== 'undefined') { // 指向public目录下的worker文件 GlobalWorkerOptions.workerSrc = '/pdf.worker.min.mjs'; // 也可以使用CDN地址,例如: // GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.worker.min.mjs'; } export default function DocumentTranslation() { const [characterCount, setCharacterCount] = useState(0); const [downloadFileName, setDownloadFileName] = useState(''); const handleFileSelect = (file: File) => { if (file.type !== 'application/pdf') return; const reader = new FileReader(); reader.onload = async (e) => { try { const typedArray = new Uint8Array(e.target.result as ArrayBuffer); // 无需额外配置worker,直接调用getDocument const pdfDoc = await getDocument({ data: typedArray }).promise; let textContent = ''; for (let i = 1; i <= pdfDoc.numPages; i++) { const page = await pdfDoc.getPage(i); const textData = await page.getTextContent(); textContent += textData.items.map(item => (item as { str: string }).str).join(' '); } setCharacterCount(textContent.length); const baseName = file.name.split('.').slice(0, -1).join('.'); setDownloadFileName(`${baseName}_processed.pdf`); } catch (err) { console.error('读取PDF失败:', err); } }; reader.readAsArrayBuffer(file); }; // ...组件渲染逻辑 return ( <div> <input type="file" accept="application/pdf" onChange={(e) => e.target.files && handleFileSelect(e.target.files[0])} /> {characterCount > 0 && <p>字符数:{characterCount}</p>} {downloadFileName && <p>处理后文件名:{downloadFileName}</p>} </div> ); }
关键注意点
- 必须添加
'use client':Remix默认是服务器组件,而FileReader、Worker等API只能在浏览器环境运行,因此组件需声明为客户端组件。 - 类型断言:
getTextContent()返回的items类型可能需要手动断言为包含str属性的对象,避免TS报错。 - 版本匹配:确保
pdfjs-dist和@types/pdfjs-dist版本一致,否则可能出现GlobalWorkerOptions声明缺失的问题。
内容的提问来源于stack exchange,提问作者YEAHH
相关产品推荐
相关产品推荐

