Zero-dependency reader for OLE2 / Compound File Binary (CFB) containers —
the storage format inside legacy Microsoft files (.xls, .doc,
.ppt, .msg, …).
List the storages and streams, and read any stream's raw bytes. Node and the browser.
Runs in Node and the browser — it only needs Uint8Array / DataView. Zero runtime
dependencies, dual ESM/CJS, fully typed, and published to npm with
provenance.
npm install ole2-reader
# or: pnpm add ole2-reader / yarn add ole2-readerLots of legacy Microsoft formats — .xls, .doc, .ppt, .msg, .vsd, MSI —
aren't single files; they're a tiny filesystem in a file, the OLE2 / Compound
File Binary container. To read any of them you first have to walk that container
and pull out its named streams.
The de-facto JS library for this, cfb, is a
SheetJS package frozen on npm since 2022, and it pulls
in extra dependencies. ole2-reader is a small, focused, actively maintained
alternative for the common case: open a compound file and read its streams. No
dependencies, MIT-licensed, and covered by npm audit / Dependabot like the rest
of your tree.
It's also hardened against hostile input — it parses untrusted binary, so it's fuzz-tested to fail with a typed error rather than crash or run out of memory.
import { readFile } from "node:fs/promises";
import { openOle2 } from "ole2-reader";
const ole = openOle2(await readFile("report.xls"));
// What's inside?
for (const entry of ole.entries()) {
console.log(entry.type, entry.name, entry.size);
// "root" "Root Entry" 0
// "stream" "Workbook" 12345
}
// Pull a stream out by name — the raw bytes, ready for a format-specific parser.
const workbook = ole.readStream("Workbook");In the browser (e.g. from a file <input>):
import { openOle2 } from "ole2-reader";
const ole = openOle2(await file.arrayBuffer());
const doc = ole.readStream("WordDocument"); // a .doc file's main streamOpens a compound file from its bytes. Throws Ole2Error if the bytes aren't a
Compound File or are structurally corrupt.
interface Ole2File {
entries(): Ole2Entry[]; // all storages and streams, in directory order
readStream(name: string): Uint8Array | undefined; // a stream's bytes, or undefined
hasStream(name: string): boolean;
}
interface Ole2Entry {
name: string;
type: "root" | "storage" | "stream";
size: number; // stream length in bytes; 0 for storages
}Both regular and mini streams are handled transparently — readStream returns
the reassembled bytes regardless of how the stream is stored.
A Compound File (OLE2, "Structured Storage", [MS-CFB]) is a hierarchical
container: storages are like folders and streams are like files, all
packed into one binary file via a FAT-style sector table. It's what Office
97–2003 used before the .xlsx/.docx ZIP formats. ole2-reader gives you the
container layer; decoding a specific stream (BIFF for .xls, the Word document
stream for .doc, MAPI properties for .msg) is the job of a format reader on
top — for example xls-reader.
- Read-only, and it reads the container only — it does not interpret the format stored inside a stream.
- Encrypted / password-protected compound files are not supported.
Bug reports, sample files, and PRs are very welcome — see CONTRIBUTING.md and the Code of Conduct. To report a security issue, see the Security Policy.
MIT © Thiago Zanluca