如何在Node.js(Express)中识别PDF为文本/图像/混合类型?
解决方案:PDF类型检测与针对性文本提取
核心思路
先通过pdf-parse提取文本,根据提取到的有效文本量判断PDF类型:
- 若有效文本量达到阈值,判定为文本型PDF,直接使用普通提取结果
- 若有效文本量极低,判定为图像型/混合型PDF,调用Tesseract OCR进行识别处理
步骤1:安装依赖
需要额外安装OCR和PDF页面渲染相关工具:
npm install tesseract.js pdfjs-dist canvas
步骤2:修改上传接口代码
更新/upload路由,加入类型检测和OCR处理逻辑:
const express = require('express'); const app = express(); const upload = require('multer')({ dest: './uploads' }); const fs = require('fs'); const path = require('path'); const pdfParse = require('pdf-parse'); const { createWorker } = require('tesseract.js'); const pdfjsLib = require('pdfjs-dist'); const pdfjsWorker = require('pdfjs-dist/build/pdf.worker.entry'); const { createCanvas } = require('canvas'); pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker; app.post("/upload", upload.single("file"), async (req, res) => { console.log(req.file); let extractedText = ''; const minTextLength = 10; // 可根据业务调整的文本阈值 try { const dataBuffer = fs.readFileSync(req.file.path); // 第一步:尝试普通文本提取 const pdfData = await pdfParse(dataBuffer); // 清理文本:去掉所有空白字符,仅保留有效内容 const cleanedText = pdfData.text.trim().replace(/\s+/g, ''); if (cleanedText.length >= minTextLength) { // 文本型PDF:直接使用提取结果 extractedText = pdfData.text; } else { // 图像/混合型PDF:启动Tesseract OCR处理 const worker = await createWorker('eng'); // 切换语言可替换为'chi_sim'等 const pdf = await pdfjsLib.getDocument({ data: dataBuffer }).promise; const numPages = pdf.numPages; // 逐页渲染为图片并识别 for (let pageNum = 1; pageNum <= numPages; pageNum++) { const page = await pdf.getPage(pageNum); const viewport = page.getViewport({ scale: 2.0 }); // 放大页面提升识别精度 const canvas = createCanvas(viewport.width, viewport.height); const context = canvas.getContext('2d'); await page.render({ canvasContext: context, viewport: viewport }).promise; const imageBuffer = canvas.toBuffer('image/png'); const { data: { text } } = await worker.recognize(imageBuffer); extractedText += `${text}\n\n`; } await worker.terminate(); } // 保存提取的文本文件 const txtFilePath = path.join('./uploads', `${req.file.originalname}.txt`); await fs.promises.writeFile(txtFilePath, extractedText); console.log("文本提取并保存成功"); res.send(`文件上传完成,文本已提取并保存为 ${req.file.originalname}.txt`); } catch (error) { console.error(error); res.status(500).send("PDF文本提取或文件保存过程中发生错误"); } }); // 原有的/search路由保持不变 app.get("/search", async (req, res) => { const keyword = req.query.keyword.toLowerCase(); const directoryPath = path.join(__dirname, "uploads"); let filesContainingKeyword = []; try { const files = await fs.promises.readdir(directoryPath); for (let file of files) { if (file.endsWith(".txt")) { let filePath = path.join(directoryPath, file); let data = await fs.promises.readFile(filePath, "utf8"); let lowerCaseData = data.toLowerCase(); if (lowerCaseData.includes(keyword)) { let index = lowerCaseData.indexOf(keyword); let preview = data.substring(index, index + 100) + "..."; let pdfFile = file.replace(".txt", ""); filesContainingKeyword.push({ filename: pdfFile, preview: preview }); console.log(`在${filePath}中找到关键词`); } else { console.log(`在${filePath}中未找到关键词`); } } } res.send(filesContainingKeyword); } catch (err) { console.error("无法扫描目录: " + err); res.status(500).send("服务器内部错误"); } }); app.listen(3000, () => console.log("服务启动在3000端口"));
关键说明
- 阈值调整:
minTextLength可根据业务场景调整,比如如果存在仅含少量文本的合法文本PDF,可适当降低阈值;若图像PDF容易产生噪声文本,可提高阈值。 - 性能优化:Tesseract OCR资源消耗较高,建议:
- 对大文件使用异步队列处理,避免阻塞主线程
- 缓存已处理的PDF结果,重复上传时直接复用
- 混合类型PDF:当前实现会对所有页面进行OCR,若需更精细化处理(仅对图像页面OCR),可通过
pdfjs的页面内容分析判断是否包含文本元素,但会增加逻辑复杂度。
内容的提问来源于stack exchange,提问作者Anandhu Remanan
相关产品推荐
相关产品推荐

