Public
Edited
Mar 14
[Archive][Tool] Extracting Compound File Binary Format Content to ZIP
[MISC] 图片叠层对比[MISC] In-browser compress with fflate
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
yield htl.html`Processing, please wait...`
if (!zip) {
yield htl.html`Drop file above`
} else {
const url = URL.createObjectURL(
new Blob([zip.buffer.transferToFixedLength()], { type: "application/zip-compressed" })
);
invalidation.then(() => {
URL.revokeObjectURL(url);
});
yield htl.html`
<a href=${url} download=${'CFB_'+files[0].name+'_'+Date.now()+'.zip'} target="_blank">Click to download</a>
`;
}
}
Insert cell
zip = {
if (!parsed?.cfb) return null
const MAX_ZIP_SIZE_IN_BYTES = 500 * 1024 * 1024
const MAX_POSSIBLE_SIZE_PER_FILE = 50 * 1024 * 1024

const promise = new Promise((resolve, reject) => {
const buffer = new ArrayBuffer(1024, { maxByteLength: MAX_ZIP_SIZE_IN_BYTES });
const blob = new Uint8Array(buffer);
let totalSize = 0;
const zip = new fflate.Zip((err, dat, final) => {
if (err) {
reject(err);
}
const offset = totalSize;
totalSize += dat.byteLength;

// make buffer resizable
if (buffer.byteLength < totalSize) {
buffer.resize(totalSize);
}
blob.set(dat, offset);

if (final) {
console.log('final: ' + final)
resolve(blob)
}
});
const cfb = parsed.cfb;
// root object
function walk(entry, parent) {
if (entry.objectType === 2) {
// stream object
const u8arr = cfbReader.readStreamObjectContent(
cfb,
Number(entry.startingSectorLocation),
Number(entry.streamSize)
);
const exts = sigs.find((it) => it.filter(u8arr))?.ext;
let suffix = "";
if (exts && !exts.some((it) => entry.name.endsWith(it))) {
suffix = "." + exts[0];
}
const file = new fflate.AsyncZipDeflate(`${parent}/${entry.name}${suffix}`);
zip.add(file);
file.push(u8arr, true);
}
if (entry.leftSibling) {
walk(entry.leftSibling, parent);
}
if (entry.rightSibling) {
walk(entry.rightSibling, parent);
}
if (entry.child) {
walk(entry.child, `${parent}/${entry.name}`);
}
}
walk(parsed.entries[0].child, "root_" + Date.now());
zip.end();
});

return await promise
}
Insert cell
Insert cell
parsed = {
if (files.length === 0) return null
const buf = await files[0].file.arrayBuffer()
const cfb = new cfbReader.CompoundFileBinary(buf)

const entries = []
let sec = 0
let dirOffset
let stop = false
while (!stop) {
try {
dirOffset = cfb.getDirectorySectorOffsetAt(sec)
} catch (err) {
stop = true
break
}
entries.push(...cfbReader.readDirectorySectorInfoV3(cfb, sec).entries)
++sec
}
for (const it of entries) {
it.child = entries[it.childID]
it.leftSibling = entries[it.leftSiblingID]
it.rightSibling = entries[it.rightSiblingID]
}
const file = cfbReader.readDirectoryEntryInfo(cfb, 10)
const blob = cfbReader.readStreamObjectContent(cfb, Number(file.startingSectorLocation), Number(file.streamSize))
// const file6 = cfbReader.readStreamObjectContent(cfb, 128, 2384)
return {
cfb,
entries,
file,
blob,
// file6
}
}
Insert cell
cfbReader = importPkg('https://pkg.pr.new/UnluckyNinja/cfb-reader@e63ab3b', 'dist/cfb-reader.browser.mjs')
Insert cell
sigs = {
return [{
filter: (typedArray)=>{
const sig = [0xFF, 0xD8, 0xFF]
if (typedArray.length < sig.length) return false
return sig.every((v,i)=>{
return typedArray[i] === v
})
},
ext: ['jpg', 'jpeg'],
},{
filter: (typedArray)=>{
const sig = [0x47, 0x49, 0x46, 0x38]
if (typedArray.length < sig.length) return false
return sig.every((v,i)=>{
return typedArray[i] === v
})
},
ext: ['gif'],
},{
filter: (typedArray)=>{
const sig = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
if (typedArray.length < sig.length) return false
return sig.every((v,i)=>{
return typedArray[i] === v
})
},
ext: ['png'],
},{
filter: (typedArray)=>{
const sig0 = [0x52, 0x49, 0x46, 0x46]
const sig8 = [0x57, 0x45, 0x42, 0x50]
if (typedArray.length < 12) return false
return sig0.every((v,i)=>{
return typedArray[i] === v
}) && sig8.every((v,i)=>{
return typedArray[i+8] === v
})
},
ext: ['webp'],
},{
filter: (typedArray)=>{
const sig = [0x42, 0x4D]
if (typedArray.length < sig.length) return false
return sig.every((v,i)=>{
return typedArray[i] === v
})
},
ext: ['bmp'],
}]
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more