zip-iterator
Version:
Extract contents from zip archive type using an iterator API using streams or paths. Use stream interface and pipe transforms to add decompression algorithms
75 lines (74 loc) • 3.3 kB
JavaScript
import { Reader } from 'zip';
import fs from 'fs';
import { Readable } from 'stream';
import zlib from 'zlib';
const decodeDateTime = (date, time)=>new Date((date >>> 9) + 1980, (date >>> 5 & 15) - 1, date & 31, time >>> 11 & 31, time >>> 5 & 63, (time & 63) * 2);
let Zip = class Zip extends Reader {
iterator() {
const stream = this;
// find the end record and read it
stream.locateEndOfCentralDirectoryRecord();
const endRecord = stream.readEndOfCentralDirectoryRecord();
// seek to the beginning of the central directory
stream.seek(endRecord.central_dir_offset);
let count = endRecord.central_dir_disk_records;
return {
next: ()=>{
if (count-- === 0) throw 'stop-iteration';
// read the central directory header
const centralHeader = stream.readCentralDirectoryFileHeader();
// save our new position so we can restore it
const saved = stream.position();
// seek to the local header and read it
stream.seek(centralHeader.local_file_header_offset);
const localHeader = stream.readLocalFileHeader();
// dont read the content just save the position for later use
const start = stream.position();
// seek back to the next central directory header
stream.seek(saved);
return {
localHeader: localHeader,
stream: stream,
start: start,
centralHeader: centralHeader,
lastModified: ()=>decodeDateTime(localHeader.last_mod_file_date, localHeader.last_mod_file_time),
getStream: ()=>{
let offset = start;
let remaining = centralHeader.compressed_size;
let res = new Readable();
res._read = function(size) {
if (remaining <= 0) return this.push(null); // done
if (size > remaining) size = remaining; // clamp
const bookmark = stream.position(); // save
stream.seek(offset);
const chunk = stream.read(size);
remaining -= size;
offset += size;
stream.seek(bookmark); // restore
this.push(chunk);
};
if (centralHeader.compression_method !== 0) res = res.pipe(zlib.createInflateRaw());
return res;
}
};
}
};
}
constructor(fd){
super(fd);
// patch pos
this._source.read = (start, length)=>{
const result = Buffer.alloc(length);
let pos = 0;
while(length > 0){
const toRead = Math.min(length, 8192);
fs.readSync(fd, result, pos, toRead, start);
length -= toRead;
start += toRead;
pos += toRead;
}
return result;
};
}
};
export { Zip as default };