上传识别PDF

这里我们通过前端来实现上传PDF并识别的功能。需要用到 react-pdf 这个包

由于需要在客户端处理PDF,客户端需要下载worker来工作,也就是需要暴露 pdfworker js文件路径给客户端。

一个基于NextJS路由的pdfworker下载方法
api/pdf.worker
import type { NextApiRequest, NextApiResponse } from 'next'
import fs from 'fs'
import path from 'path'
import { findSpecificDir } from '@/utils/serverUtil'

const PdfWorker = async (req: NextApiRequest, res: NextApiResponse) => {
    const rootDir = await findSpecificDir({ startPath: __dirname, specificFile: 'package.json' })
    const pdfworkerjs = path.resolve(rootDir, './node_modules/pdfjs-dist/build/pdf.worker.js')

    fs.readFile(pdfworkerjs, (err, data) => {
        if (err) {
            res.setHeader('Content-Type', 'text/plain')
            res.status(404)
            res.end('404 Not Found\n')
            return
        }
        res.setHeader('Content-Type', 'application/javascript')
        res.write(data)
        res.end()
    })
}

export default PdfWorker

先引入 react-pdf,并且指定 pdfworker.js

客户端
import { Document, Page, pdfjs } from 'react-pdf' //'react-pdf'

// 设置 PDF.js 的 workerSrc
pdfjs.GlobalWorkerOptions.workerSrc = '/api/pdf.worker'

这里Document 为上传PDF的主体,Page为每一页。

客户端
const PdfReader = ({
    file,
    pageCallback,
    loadCallback,
}: {
    file: any
    pageCallback?: (arg?: any) => void
    loadCallback?: (arg?: any) => void
}) => {
    const [numPages, setNumPages] = useState(0)

    const onDocumentLoadSuccess = (documentArgs: DocumentCallback) => {
        const { numPages } = documentArgs || {}
        setNumPages(numPages)
        if (typeof loadCallback == `function`) {
            loadCallback()
        }
    }

    let tempCollection: PageContentCollection = []
    const onPageTextLoadSuccess = (page: PageCallback) => {
        page.getTextContent().then(textContent => {
            tempCollection.push({
                page: page.pageNumber,
                contentList: textContent?.items,
            })
            if (tempCollection.length >= numPages && numPages > 0) {
                // page Load Success
                const longContentBlock = getLongContentFromPage(tempCollection)
                const { longParagraph } = longContentBlock
                if (longParagraph && typeof pageCallback == `function`) {
                    pageCallback(longParagraph)
                }
            }
        })
    }

    return (
        <div>
            <Document file={file} onLoadSuccess={onDocumentLoadSuccess}>
                {_.map(new Array(numPages), (el, index) => (
                    <div className="inside_carousel" key={`pdf_page_${index}`}>
                        <Page
                            key={`page_${index + 1}`}
                            pageNumber={index + 1}
                            renderTextLayer={false}
                            renderAnnotationLayer={false}
                            onLoadSuccess={page => onPageTextLoadSuccess(page)}
                        />
                        <br />
                    </div>
                ))}
            </Document>
        </div>
    )
}

这里客户端的PDF view渲染的时候,每一页渲染都会触发 Page.onLoadSuccess。需要在这里收集每一页的内容整理之后处理。 页面上的文本内容在 page.getTextContent()中,但实际上他识别到的并不是一段话,而是PDF上每个文字的点位。

下一节我们就需要通过根据这个点位来处理/识别文字,并且进行分割。

最后更新于