Node.js 流式 API
pdf-oxide 原生绑定为搜索结果、页面和表格提供可读流——既符合 Node.js 管道的习惯用法,又能在处理大型文档时高效利用内存。
所有流都以对象模式实现标准的 Node.js Readable 接口,支持背压,可与 pipe() 集成,并支持 for await 异步迭代。
流是 Node.js 绑定的原生特性。对于 WASM 构建,请改用同步迭代。
SearchStream
随着底层 SearchManager 产生匹配项,逐个发出 SearchResult。
const { PdfDocument, SearchManager, SearchStream } = require("pdf-oxide");
const doc = new PdfDocument("large.pdf");
const manager = new SearchManager(doc);
const stream = new SearchStream(manager, "invoice");
stream.on("data", (r) => {
console.log(`page ${r.pageIndex + 1}: ${r.text}`);
});
stream.on("end", () => {
console.log("search complete");
doc.close();
});
stream.on("error", (err) => {
console.error(err);
doc.close();
});
区分大小写的搜索
const stream = new SearchStream(manager, "Invoice", { caseSensitive: true });
异步迭代
for await (const result of stream) {
if (result.pageIndex > 50) break;
console.log(result.text);
}
pipe() 兼容性
const { Writable } = require("stream");
const sink = new Writable({
objectMode: true,
write(result, _enc, cb) {
console.log(`${result.pageIndex}:${result.text}`);
cb();
},
});
stream.pipe(sink);
PageIteratorStream
逐页发出提取出的文本。适用于按行输出的场景,或在以限流队列向 LLM 投喂内容时使用。
const { PageIteratorStream } = require("pdf-oxide");
const stream = new PageIteratorStream(doc, { format: "markdown" });
for await (const { pageIndex, content } of stream) {
await indexPage(pageIndex, content);
}
format 接受 "text"(默认)、"markdown"、"html"、"plain"。
TableStream
每检测到一个表格就发出一个。
const { TableStream } = require("pdf-oxide");
const stream = new TableStream(doc);
stream.on("data", (table) => {
console.log(`${table.rows.length}x${table.rows[0].length} on page ${table.pageIndex}`);
});
背压
所有流都实现了标准的 Node.js 背压机制。如果消费方处理较慢,流会暂停提取,直到 .read() 恢复:
stream.on("data", async (result) => {
stream.pause();
await slowIndex(result);
stream.resume();
});
或者使用 for await,它会自动处理暂停。
错误处理
提取过程中的错误以标准 error 事件的形式发出:
stream.on("error", (err) => {
if (err.code === "PDF_INVALID_PAGE") {
console.warn("skipping invalid page", err.pageIndex);
} else {
throw err;
}
});
内存效率
流在任意时刻只保留一个结果。对于一个产生 50,000 个匹配项的 10,000 页 PDF,SearchStream 占用的内存保持恒定——整个结果集永远不会被一次性物化。
清理
关闭父级 PdfDocument 会结束所有关联的流。流还会在 end / error 时清理其对 manager 的引用。
const doc = new PdfDocument("big.pdf");
const stream = new SearchStream(new SearchManager(doc), "TODO");
stream.on("end", () => doc.close());
stream.on("error", () => doc.close());
在 Node.js 22+ 中,using 关键字会在作用域退出时释放文档:
{
using doc = new PdfDocument("big.pdf");
const stream = new SearchStream(new SearchManager(doc), "TODO");
for await (const r of stream) console.log(r);
} // doc.close() called automatically
相关链接
- Node.js 快速上手 — 安装、快速开始
- Node.js API 参考
- 搜索 — 非流式搜索选项