ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用了Trae CN,原来实现从PDF提取页面如此简单:TaoToken统一Key打通react-pdf与pdf-lib

用了Trae CN,原来实现从PDF提取页面如此简单:TaoToken统一Key打通react-pdf与pdf-lib 1. 从整份 PDF 里抠出指定几页为什么值得单独做个小工具你手上大概率遇到过这种场景合作方发来一份 80 页的产品手册你只想要第 12 到 18 页或者一份合同扫描件你只需要签字页和附件页。传统做法是打开在线拆分工具先按范围拆成几个小文件再挑出需要的重新合并。文件要上传到别人的服务器步骤还绕拆完合完自己都记不清哪份是哪份。我这次要聊的是在 Trae CN 里用 react-pdf 做预览、用 pdf-lib 做导出做一个纯前端的 PDF 指定页面提取工具。整个流程文件不离开浏览器选页、预览、导出一次完成。react-pdf 负责把 PDF 渲染成可交互的缩略图pdf-lib 负责把选中的页面复制进一份新文档并触发下载。适合谁适合正在用 Trae CN 做前端小工具、想搞懂 PDF 页面级操作、又不想把文件传到第三方服务的人。这里有个容易被忽略的点react-pdf 和 pdf-lib 是两个定位完全不同的库。react-pdf 底层是 pdfjs强项是渲染和交互它不负责生成新 PDFpdf-lib 强项是读写 PDF 结构、复制页面、嵌入内容但它不做可视化预览。把两者拼起来才能实现“看得见地选页选完能导出”。很多人卡住就是因为指望一个库把两件事都干了。另外开发过程中我会用 TaoToken 的统一 Key 来调用模型做代码补全和报错分析。它把多家模型的调用收敛成一个 API 通道Base URL 和 Key 配一次切换模型只改 Model ID省得在多个平台之间来回倒腾配置。下面从环境准备开始一步步把这条链路跑通。2. TaoToken 前置准备统一 Key 与 API 通道怎么配在动手写组件之前先把模型调用的通道配好。TaoToken 的定位是统一 API 入口你拿到一个 Key就能通过同一个 Base URL 访问不同模型。对做前端工具的人来说好处是调试阶段让模型帮忙看报错、补类型定义时不用为每个模型单独维护一套鉴权逻辑。先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册并进入控制台。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在里面创建 API Key。创建完先复制保存Key 只在创建时完整显示一次。API 的基础地址是 https://taotoken.net/api 注意这个地址不带任何查询参数直接作为 Base URL 使用。如果你用的是 OpenAI 兼容的 SDK把 baseURL 指向它即可如果是自己发 fetch 请求就拼上 /v1/chat/completions 这类路径。模型 ID 怎么选做代码补全和报错分析选一个上下文长、代码能力稳的就行。你可以在模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 里实际发几条消息验证确认这个 Model ID 能正常返回再写进配置。这一步别省我见过有人配置里 Model ID 拼错一个字符请求一直 404排查半天以为是网络问题。如果你打算长期做编码类任务比如让模型持续帮你重构组件、写测试可以了解下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合高频、长周期的编码场景。只是偶尔问几个报错用按量调用就够了。配置的时候记住三件套Base URL 填 https://taotoken.net/api 鉴权用你创建的 KeyModel ID 填你在模型对话里验证过能用的那个。这三样在后面的环境变量里会用到。Key 不要硬编码进前端代码提交到仓库本地开发放 .env.local构建时通过环境变量注入。3. 可复制配置依赖安装与 react-pdf pdf-lib 组件代码这一节是核心给出可以直接复制的配置和代码。假设你已经在 Trae CN 里建好了一个 React TypeScript 项目用 Vite 或 Next.js 都行下面以 Vite 为例。先装依赖。react-pdf 需要配套的 pdfjs workerpdf-lib 单独装npm install react-pdf pdf-lib npm install -D types/react-pdfreact-pdf 的版本要和 pdfjs 匹配装完看一下 package.json 里 react-pdf 的版本它内部会带一个对应版本的 pdfjs-dist。如果 worker 报版本不一致就手动装同版本的 pdfjs-distnpm install pdfjs-dist与react-pdf匹配的版本环境变量文件 .env.local把 TaoToken 的三件套写进去VITE_TAOTOKEN_BASE_URLhttps://taotoken.net/api VITE_TAOTOKEN_API_KEY你的Key VITE_TAOTOKEN_MODEL_ID你验证过的ModelID然后是 PDF 预览组件。核心是用 react-pdf 的 Document 和 Page 渲染缩略图用 pdf-lib 做导出。下面是一个精简但可运行的 PdfPagePicker.tsximport { useState, useCallback } from react; import { Document, Page, pdfjs } from react-pdf; import { PDFDocument } from pdf-lib; import react-pdf/dist/Page/AnnotationLayer.css; import react-pdf/dist/Page/TextLayer.css; pdfjs.GlobalWorkerOptions.workerSrc new URL( pdfjs-dist/build/pdf.worker.min.mjs, import.meta.url ).toString(); type Props { fileUrl: string }; export default function PdfPagePicker({ fileUrl }: Props) { const [numPages, setNumPages] useState(0); const [selected, setSelected] useStateSetnumber(new Set()); const onLoadSuccess useCallback(({ numPages }: { numPages: number }) { setNumPages(numPages); }, []); const togglePage (pageNumber: number) { setSelected((prev) { const next new Set(prev); if (next.has(pageNumber)) next.delete(pageNumber); else next.add(pageNumber); return next; }); }; const exportSelected async () { if (selected.size 0) return; const existingBytes await fetch(fileUrl).then((r) r.arrayBuffer()); const srcDoc await PDFDocument.load(existingBytes); const newDoc await PDFDocument.create(); const indices Array.from(selected) .sort((a, b) a - b) .map((n) n - 1); const copied await newDoc.copyPages(srcDoc, indices); copied.forEach((p) newDoc.addPage(p)); const bytes await newDoc.save(); const blob new Blob([bytes], { type: application/pdf }); const url URL.createObjectURL(blob); const a document.createElement(a); a.href url; a.download extracted.pdf; a.click(); URL.revokeObjectURL(url); }; return ( div classNamepdf-picker Document file{fileUrl} onLoadSuccess{onLoadSuccess} div classNamethumb-grid {Array.from({ length: numPages }, (_, i) i 1).map((n) ( div key{n} className{selected.has(n) ? thumb selected : thumb} onClick{() togglePage(n)} Page pageNumber{n} width{140} / span{n}/span /div ))} /div /Document button disabled{selected.size 0} onClick{exportSelected} 导出选中 {selected.size} 页 /button /div ); }几个关键点解释一下。pdfjs.GlobalWorkerOptions.workerSrc 必须设置否则 react-pdf 会报 worker 加载失败。用 new URL(..., import.meta.url) 的方式让打包工具正确处理 worker 路径比写死 CDN 地址稳。copyPages 接收的是从 0 开始的索引数组所以要把页码减 1并且排序保证导出顺序和原文档一致。如果你在 Trae CN 里让模型帮你补这段代码可以把上面的类型定义和注释一起给它让它基于 existing code 做增量修改而不是重新生成整个文件。这样既省 token也避免它把你已经调好的 worker 配置覆盖掉。4. 验证请求从选页到下载新 PDF 跑通一次代码写完后跑起来验证。启动开发服务器npm run dev浏览器打开本地地址上传一份多页 PDF。正常情况下你会看到缩略图网格点击任意几页缩略图出现选中态底部按钮显示“导出选中 N 页”。点导出浏览器直接下载 extracted.pdf打开确认只包含你选的那几页顺序和原文档一致。这一步如果成功说明 react-pdf 渲染和 pdf-lib 导出两条链路都通了。接下来验证 TaoToken 的模型调用通道。写一个最小的请求脚本确认 Key 和 Base URL 配置正确curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $VITE_TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $VITE_TAOTOKEN_MODEL_ID, messages: [{role: user, content: 用一句话说明 pdf-lib 的 copyPages 作用}] }返回里能看到 choices 数组和模型输出就说明通道正常。这个验证很重要因为后面你让模型帮忙分析 react-pdf 的报错时靠的就是这条通道。如果返回 401检查 Key 是否复制完整、有没有多余空格如果返回 404检查 Model ID 是否和模型对话页面里验证过的一致。实测下来整个流程最花时间的不是写代码而是调 react-pdf 的渲染参数。缩略图宽度设太小看不清内容设太大一页文档要滚动很久。我一般用 140 到 160 像素配合 CSS grid 自适应列数。另外 Page 组件渲染大量页面时会有性能压力如果文档超过 50 页建议做虚拟滚动或者分页加载不要一次性渲染所有 Page。导出环节有个细节pdf-lib 的 save() 返回 Uint8Array直接丢给 Blob 就行。如果你需要保留原文档的表单域或书签copyPages 默认只复制页面内容表单和书签不会带过去。这个在提取页面场景下通常没问题但如果你有特殊需求要提前确认。5. 本篇常见错排查401、worker 加载失败与 choices 读取异常这一节把几个高频报错对照着说清楚都是我在实际开发里踩过的。第一个401 Unauthorized。这个基本都出在 TaoToken 的 Key 上。检查 .env.local 里的 VITE_TAOTOKEN_API_KEY 有没有多余空格或换行Vite 的环境变量在客户端是通过 import.meta.env 读取的变量名必须以 VITE_ 开头否则读不到。如果你在代码里直接写 process.env在 Vite 里是拿不到的。另外确认请求头是 Authorization: Bearer Bearer 和 Key 之间有一个空格。第二个local proxy failed 或 worker 加载失败。react-pdf 报这个通常是 workerSrc 没设对。检查 pdfjs.GlobalWorkerOptions.workerSrc 是否在组件渲染前设置以及路径是否指向实际存在的 worker 文件。用 Vite 的话new URL(pdfjs-dist/build/pdf.worker.min.mjs, import.meta.url) 这种方式最稳。如果还是失败看控制台具体报的是 404 还是版本不匹配版本不匹配就装和 react-pdf 同版本的 pdfjs-dist。第三个reading choices 报错。这个出现在你解析模型返回时代码里写了 response.choices[0]但实际返回结构不是预期的那样。先打印完整 response 看结构确认是不是被包了一层 data或者返回的是流式格式。如果是流式choices 在每一个 chunk 里要逐块拼接。另外确认请求体里 stream 字段没有误设成 true。第四个OAuth 相关报错。如果你在配置里误用了 OAuth 流程而不是 API Key会看到 token 获取失败之类的提示。TaoToken 的 API 调用用 Key 鉴权就够了不需要走 OAuth。检查你的请求是不是被某个 SDK 自动加了 OAuth 头把它去掉。第五个导出 PDF 空白或页数不对。检查 copyPages 的索引是不是从 0 开始以及有没有对选中页码排序。如果导出后页面顺序乱了就是没排序如果导出空白可能是源文档加载失败existingBytes 是空的。在 fetch 后面加个长度判断确保拿到的是有效 PDF 字节。排查的时候把完整报错信息贴给模型让它结合你的代码上下文分析比你自己猜快很多。这也是配好 TaoToken 通道的价值所在。6. 把这条链路用起来从选页工具到更多 PDF 操作走到这里你已经有了一个能跑的 PDF 指定页面提取工具。它的价值不只是省掉在线拆分合并的步骤更在于文件全程在浏览器里处理不上传服务器对合同、证件这类敏感文档更放心。如果你想继续扩展几个方向可以试试。一是加页面拖拽排序导出前调整页面顺序pdf-lib 的 copyPages 支持按你给的索引顺序复制改一下 indices 数组就行。二是加页码范围输入框支持“1-5,8,10-12”这种写法解析成索引数组后复用同一套导出逻辑。三是加多文档合并把多个 PDF 的选中页拼进一份新文档pdf-lib 的 copyPages 可以跨文档复制只要分别 load 再 copy 到同一个 newDoc 里。开发过程中Trae CN 的 existing code 能力确实省事它基于你已有的文件做增量修改不用每次输出全量代码。配合 TaoToken 的统一 Key模型调用和代码补全的配置收敛成一套切换模型只改 Model ID。需要看接入细节的话接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite API Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。先把 worker 配置和导出逻辑调稳再往上叠交互这条路我走过顺序别反。
返回列表