mirror of
https://github.com/codedread/bitjs
synced 2025-10-03 09:39:16 +02:00
130 lines
No EOL
3.5 KiB
JavaScript
130 lines
No EOL
3.5 KiB
JavaScript
|
|
import { ByteBuffer } from '../io/bytebuffer.js';
|
|
|
|
// NOTE: THIS IS A VERY HACKY WORK-IN-PROGRESS! THE API IS NOT FROZEN! USE AT YOUR OWN RISK!
|
|
|
|
/**
|
|
* @typedef FileInfo An object that is sent to the worker to represent a file to zip.
|
|
* @property {string} fileName The name of the file. TODO: Includes the path?
|
|
* @property {number} lastModTime The number of ms since the Unix epoch (1970-01-01 at midnight).
|
|
* @property {ArrayBuffer} fileData The bytes of the file.
|
|
*/
|
|
|
|
/**
|
|
* @typedef CompressorOptions
|
|
* @property {string} pathToBitJS A string indicating where the BitJS files are located.
|
|
*/
|
|
|
|
/**
|
|
* @readonly
|
|
* @enum {string}
|
|
*/
|
|
export const CompressStatus = {
|
|
NOT_STARTED: 'not_started',
|
|
READY: 'ready',
|
|
WORKING: 'working',
|
|
COMPLETE: 'complete',
|
|
ERROR: 'error',
|
|
};
|
|
|
|
/**
|
|
* A thing that zips files.
|
|
* NOTE: THIS IS A VERY HACKY WORK-IN-PROGRESS! THE API IS NOT FROZEN! USE AT YOUR OWN RISK!
|
|
*/
|
|
export class Zipper {
|
|
/**
|
|
* @param {CompressorOptions} options
|
|
*/
|
|
constructor(options) {
|
|
/**
|
|
* The path to the BitJS files.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
this.pathToBitJS = options.pathToBitJS || '/';
|
|
|
|
/**
|
|
* Private web worker initialized during start().
|
|
* @type {Worker}
|
|
* @private
|
|
*/
|
|
this.worker_ = null;
|
|
|
|
/**
|
|
* @type {CompressStatus}
|
|
* @private
|
|
*/
|
|
this.compressState = CompressStatus.NOT_STARTED;
|
|
|
|
/**
|
|
* @type {Uint8Array}
|
|
* @private
|
|
*/
|
|
this.byteArray = new Uint8Array(0);
|
|
}
|
|
|
|
/**
|
|
* Must only be called on a Zipper that has been started. See start().
|
|
* @param {FileInfo[]} files
|
|
* @param {boolean} isLastFile
|
|
*/
|
|
appendFiles(files, isLastFile) {
|
|
if (!this.worker_) {
|
|
throw `Worker not initialized. Did you forget to call start() ?`;
|
|
}
|
|
if (![CompressStatus.READY, CompressStatus.WORKING].includes(this.compressState)) {
|
|
throw `Zipper not in the right state: ${this.compressState}`;
|
|
}
|
|
|
|
this.worker_.postMessage({ files, isLastFile });
|
|
}
|
|
|
|
/**
|
|
* @param {FileInfo[]} files
|
|
* @param {boolean} isLastFile
|
|
* @returns {Promise<Uint8Array>} A Promise that contains the entire zipped archive.
|
|
*/
|
|
start(files, isLastFile) {
|
|
return new Promise((resolve, reject) => {
|
|
this.worker_ = new Worker(this.pathToBitJS + `archive/zip.js`);
|
|
this.worker_.onerror = (evt) => {
|
|
console.log('Worker error: message = ' + evt.message);
|
|
throw evt.message;
|
|
};
|
|
this.worker_.onmessage = (evt) => {
|
|
if (typeof evt.data == 'string') {
|
|
// Just log any strings the worker pumps our way.
|
|
console.log(evt.data);
|
|
} else {
|
|
switch (evt.data.type) {
|
|
case 'start':
|
|
this.compressState = CompressStatus.WORKING;
|
|
break;
|
|
case 'finish':
|
|
this.compressState = CompressStatus.COMPLETE;
|
|
resolve(this.byteArray);
|
|
break;
|
|
case 'compress':
|
|
this.addBytes_(evt.data.bytes);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
this.compressState = CompressStatus.READY;
|
|
this.appendFiles(files, isLastFile);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Updates the internal byte array with new bytes (by allocating a new array and copying).
|
|
* @param {Uint8Array} newBytes
|
|
* @private
|
|
*/
|
|
addBytes_(newBytes) {
|
|
const oldArray = this.byteArray;
|
|
this.byteArray = new Uint8Array(oldArray.byteLength + newBytes.byteLength);
|
|
this.byteArray.set(oldArray);
|
|
this.byteArray.set(newBytes, oldArray.byteLength);
|
|
}
|
|
} |