Tools for finding and accessing files and directories within CFB containers.
Finds a file entry by path or filename within a CFB container. The search is case-insensitive and can match both full paths and just filenames.
/**
* Find a file entry by path or filename (case-insensitive)
* @param cfb - CFB container to search in
* @param path - File path or filename to find
* @returns Found entry or null if not found
*/
function find(cfb: CFB$Container, path: string): CFB$Entry | null;
interface CFB$Entry {
/** Case-sensitive internal name */
name: string;
/** CFB type (see CFB$EntryType enum) */
type: number;
/** Raw content as buffer or array */
content: CFB$Blob;
/** Creation time (if available) */
ct?: Date;
/** Modification time (if available) */
mt?: Date;
/** Red/Black Tree color: 0 = red, 1 = black */
color: number;
/** Class ID represented as hex string */
clsid: string;
/** User-defined state bits */
state: number;
/** Starting sector in the file */
start: number;
/** Data size in bytes */
size: number;
/** Storage location (fat or minifat) */
storage?: string;
/** Content type (used for MAD format) */
ctype?: string;
}
enum CFB$EntryType {
unknown,
storage,
stream,
lockbytes,
property,
root
}Usage Examples:
const CFB = require('cfb');
// Read a CFB file
const cfb = CFB.read('document.xls', { type: 'file' });
// Find by exact path
const workbook = CFB.find(cfb, 'Workbook');
if (workbook) {
console.log('Found workbook:', workbook.name);
console.log('Size:', workbook.size, 'bytes');
console.log('Type:', workbook.type); // 2 = stream
}
// Find by partial path (case-insensitive)
const summary = CFB.find(cfb, 'summaryinformation');
if (summary) {
console.log('Found summary:', summary.name);
}
// Find in nested structure
const nestedFile = CFB.find(cfb, 'Dir1/Dir2/MyFile');
if (nestedFile) {
console.log('Content:', nestedFile.content);
}Access container structure directly through the container properties.
interface CFB$Container {
/** List of all file/storage paths in the container */
FullPaths: string[];
/** Array of file entries corresponding to FullPaths */
FileIndex: CFB$Entry[];
/** Raw binary data structure (when parsed with raw: true) */
raw?: {
header: CFB$Blob;
sectors: CFB$Blob[];
};
}Usage Examples:
const CFB = require('cfb');
// Read CFB file
const cfb = CFB.read('document.xls', { type: 'file' });
// List all paths
console.log('All paths in container:');
cfb.FullPaths.forEach((path, index) => {
const entry = cfb.FileIndex[index];
console.log(`${path} (${entry.size} bytes, type: ${entry.type})`);
});
// Find all streams (type 2)
const streams = cfb.FileIndex.filter(entry => entry.type === 2);
console.log('Found', streams.length, 'streams');
// Find all storages (type 1)
const storages = cfb.FileIndex.filter(entry => entry.type === 1);
console.log('Found', storages.length, 'storages');
// Access by index
const firstEntry = cfb.FileIndex[0];
console.log('First entry:', firstEntry.name);Understanding and working with different entry types in CFB containers.
enum CFB$EntryType {
unknown = 0, // Unknown entry type
storage = 1, // Directory/folder
stream = 2, // File/data stream
lockbytes = 3, // Lock bytes object
property = 4, // Property storage
root = 5 // Root storage
}
enum CFB$StorageType {
fat = 0, // Regular FAT storage
minifat = 1 // Mini FAT storage (for small streams)
}Usage Examples:
const CFB = require('cfb');
// Read and analyze CFB structure
const cfb = CFB.read('document.xls', { type: 'file' });
// Categorize entries by type
const categorized = {
root: [],
storages: [],
streams: [],
unknown: []
};
cfb.FileIndex.forEach(entry => {
switch (entry.type) {
case 5: // root
categorized.root.push(entry);
break;
case 1: // storage
categorized.storages.push(entry);
break;
case 2: // stream
categorized.streams.push(entry);
break;
default:
categorized.unknown.push(entry);
}
});
console.log('Structure analysis:');
console.log('- Root entries:', categorized.root.length);
console.log('- Storages (directories):', categorized.storages.length);
console.log('- Streams (files):', categorized.streams.length);
console.log('- Unknown types:', categorized.unknown.length);
// Find large streams
const largeStreams = categorized.streams.filter(stream => stream.size > 1000);
console.log('Large streams (>1KB):');
largeStreams.forEach(stream => {
console.log(`- ${stream.name}: ${stream.size} bytes`);
});The find function supports various path matching patterns for flexible file location.
Usage Examples:
const CFB = require('cfb');
const cfb = CFB.read('document.xls', { type: 'file' });
// Exact name matching (case-insensitive)
const workbook = CFB.find(cfb, 'Workbook');
// Path-based matching
const docSummary = CFB.find(cfb, '\x05DocumentSummaryInformation');
// Common Office document streams
const commonStreams = [
'Workbook', // Excel workbook data
'WorkbookHdr', // Excel workbook header
'WordDocument', // Word document stream
'PowerPoint Document', // PowerPoint presentation
'SummaryInformation', // Document properties
'DocumentSummaryInformation' // Extended properties
];
commonStreams.forEach(streamName => {
const stream = CFB.find(cfb, streamName);
if (stream) {
console.log(`Found ${streamName}: ${stream.size} bytes`);
}
});
// Search for streams with specific patterns
const allEntries = cfb.FileIndex.filter(entry => entry.type === 2);
const worksheets = allEntries.filter(entry =>
entry.name.toLowerCase().includes('sheet')
);
console.log('Found worksheet-related streams:', worksheets.length);For complex CFB structures with nested storages:
function traverseStorage(cfb, storagePath = '') {
const results = [];
cfb.FullPaths.forEach((path, index) => {
const entry = cfb.FileIndex[index];
// Skip root entry
if (entry.type === 5) return;
// Check if this path is under the specified storage
if (storagePath === '' || path.startsWith(storagePath)) {
results.push({
path: path,
entry: entry,
isStorage: entry.type === 1,
isStream: entry.type === 2
});
}
});
return results;
}
// Usage
const cfb = CFB.read('complex-document.cfb', { type: 'file' });
const allItems = traverseStorage(cfb);
const rootItems = traverseStorage(cfb, '');
console.log('Total items:', allItems.length);Search for entries based on content patterns:
function findByContentPattern(cfb, pattern) {
const matches = [];
cfb.FileIndex.forEach((entry, index) => {
if (entry.type === 2 && entry.content) { // streams only
const contentStr = Buffer.from(entry.content).toString('binary');
if (contentStr.includes(pattern)) {
matches.push({
path: cfb.FullPaths[index],
entry: entry
});
}
}
});
return matches;
}
// Usage
const cfb = CFB.read('document.xls', { type: 'file' });
const xmlEntries = findByContentPattern(cfb, '<?xml');
console.log('Found XML content in', xmlEntries.length, 'streams');