Skip to content

Node.js-Streams-API

Das native pdf-oxide-Binding bietet Readable-Streams für Suchergebnisse, Seiten und Tabellen — idiomatisch für Node.js-Pipelines und speichereffizient bei großen Dokumenten.

Alle Streams implementieren die standardmäßige Node.js-Readable-Schnittstelle im Objektmodus, unterstützen Backpressure, lassen sich mit pipe() integrieren und funktionieren mit der for await-Async-Iteration.

Streams sind im Node.js-Binding nativ verfügbar. Beim WASM-Build iterieren Sie synchron.

SearchStream

Gibt jeweils ein SearchResult aus, sobald der zugrunde liegende SearchManager Treffer produziert.

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();
});

Groß-/Kleinschreibung beachtende Suche

const stream = new SearchStream(manager, "Invoice", { caseSensitive: true });

Async-Iteration

for await (const result of stream) {
  if (result.pageIndex > 50) break;
  console.log(result.text);
}

pipe()-Kompatibilität

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

Gibt jeweils den extrahierten Text einer Seite aus. Nützlich für zeilenorientierte Ausgaben oder wenn ein LLM über eine ratenbegrenzte Warteschlange gespeist wird.

const { PageIteratorStream } = require("pdf-oxide");

const stream = new PageIteratorStream(doc, { format: "markdown" });

for await (const { pageIndex, content } of stream) {
  await indexPage(pageIndex, content);
}

format akzeptiert "text" (Standard), "markdown", "html", "plain".

TableStream

Gibt jeweils eine Tabelle aus, sobald sie erkannt wird.

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}`);
});

Backpressure

Alle Streams implementieren das standardmäßige Node.js-Backpressure. Wenn Ihr Consumer langsam ist, pausiert der Stream die Extraktion, bis .read() fortgesetzt wird:

stream.on("data", async (result) => {
  stream.pause();
  await slowIndex(result);
  stream.resume();
});

Oder verwenden Sie for await, das das Pausieren automatisch übernimmt.

Fehlerbehandlung

Fehler während der Extraktion werden als standardmäßige error-Events ausgegeben:

stream.on("error", (err) => {
  if (err.code === "PDF_INVALID_PAGE") {
    console.warn("skipping invalid page", err.pageIndex);
  } else {
    throw err;
  }
});

Speichereffizienz

Streams halten immer nur ein Ergebnis gleichzeitig vor. Bei einem 10.000-seitigen PDF mit 50.000 Treffern verbraucht ein SearchStream konstant viel Speicher — die gesamte Ergebnismenge wird nie materialisiert.

Aufräumen

Das Schließen des übergeordneten PdfDocument beendet alle angehängten Streams. Streams geben außerdem ihre Manager-Referenz bei end / error frei.

const doc = new PdfDocument("big.pdf");
const stream = new SearchStream(new SearchManager(doc), "TODO");

stream.on("end", () => doc.close());
stream.on("error", () => doc.close());

Ab Node.js 22 gibt das Schlüsselwort using das Dokument frei, sobald der Gültigkeitsbereich verlassen wird:

{
  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

Verwandte Themen