Added buffer to file. Added installation and better usage instructions.

master 0.0.2
Qvazar 2015-09-14 18:34:34 +02:00
rodzic b2edee96ad
commit 487a977047
8 zmienionych plików z 387 dodań i 39 usunięć

1
.gitignore vendored
Wyświetl plik

@ -1,2 +1 @@
build
node_modules

Wyświetl plik

@ -11,38 +11,40 @@ Useful when packing all your application images/sound/json/etc. data in a standa
As of September 2015 this includes Chrome>=20, Firefox>=13, IE>=10, Opera>=12.10 and Safari>=8.
[Web Worker transferable objects](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) are used when available, increasing speed greatly. This is supported in Chrome>=21, Firefox>=18, Opera>=15 and Safari.
## Installation
bower install --save js-untar
## Documentation
Load the module with RequireJS or similar. The module is a function that returns a modified Promise with a progress callback.
Supports AMD, CommonJS or simply load with a script tag, which will provide a global untar function.
The module is a function that returns a modified Promise with a progress callback.
This callback is executed every time a file is extracted.
The standard Promise.then method is also called when extraction is done, with all extracted files as argument.
The extraction is done in a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) to allow the main UI thread to continue.
### Example:
define(["untar"], function(untar) {
// Load the source ArrayBuffer from a XMLHttpRequest (or any other way you may need).
var sourceBuffer = [...];
untar(sourceBuffer)
.progress(function(extractedFile) {
...
})
.then(function(extractedFiles) {
...
});
// or
untar(sourceBuffer).then(
function(extractedFiles) { // onSuccess
...
},
function(err) { // onError
...
},
function(extractedFile) { // onProgress
...
}
);
// Load the source ArrayBuffer from a XMLHttpRequest (or any other way you may need).
var sourceBuffer = [...];
untar(sourceBuffer)
.progress(function(extractedFile) {
... // Do something with a single extracted file.
})
.then(function(extractedFiles) {
... // Do something with all extracted files.
});
// or
untar(sourceBuffer).then(
function(extractedFiles) { // onSuccess
... // Do something with all extracted files.
},
function(err) { // onError
... // Handle the error.
},
function(extractedFile) { // onProgress
... // Do something with a single extracted file.
}
);
### File object
The returned file object(s) has the following properties. Most of these are explained in the [Tar wikipedia entry](https://en.wikipedia.org/wiki/Tar_(computing)#File_format).
@ -57,7 +59,8 @@ The returned file object(s) has the following properties. Most of these are expl
* type
* linkname
* ustarFormat
* blob A [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) object with the contens of the file.
* buffer An ArrayBuffer with the contents of the file.
* blob A [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) object with the contents of the file.
* getObjectUrl()
A unique [ObjectUrl](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) to the data can be retrieved with this method for easy usage of extracted data in <img> tags etc.
document.getElementById("targetImageElement").src = file.getObjectUrl();

Wyświetl plik

@ -0,0 +1,65 @@
;(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.ProgressivePromise = factory();
}
}(this, function() {
"use strict";
/* globals window: false, Promise: false */
/**
Returns a Promise decorated with a progress() event.
*/
function ProgressivePromise(fn) {
if (typeof Promise !== "function") {
throw new Error("Promise implementation not available in this environment.");
}
var progressCallbacks = [];
var progressHistory = [];
function doProgress(value) {
for (var i = 0, l = progressCallbacks.length; i < l; ++i) {
progressCallbacks[i](value);
}
progressHistory.push(value);
}
var promise = new Promise(function(resolve, reject) {
fn(resolve, reject, doProgress);
});
promise.progress = function(cb) {
if (typeof cb !== "function") {
throw new Error("cb is not a function.");
}
// Report the previous progress history
for (var i = 0, l = progressHistory.length; i < l; ++i) {
cb(progressHistory[i]);
}
progressCallbacks.push(cb);
return promise;
};
var origThen = promise.then;
promise.then = function(onSuccess, onFail, onProgress) {
origThen.call(promise, onSuccess, onFail);
if (onProgress !== undefined) {
promise.progress(onProgress);
}
return promise;
};
return promise;
}
return ProgressivePromise;
}));

Wyświetl plik

@ -0,0 +1,189 @@
"use strict";
/* globals postMessage: false, DataView: false, self: false, window: false, ArrayBuffer: false, Uint8Array: false */
function UntarWorker() {
}
UntarWorker.prototype = {
onmessage: function(msg) {
try {
if (msg.data.type === "extract") {
this.untarBuffer(msg.data.buffer);
} else {
throw new Error("Unknown message type: " + msg.data.type);
}
} catch (err) {
this.postError(err);
}
},
postError: function(err) {
//console.info("postError(" + err.message + ")" + " " + JSON.stringify(err));
this.postMessage({ type: "error", data: { message: err.message } });
},
postLog: function(level, msg) {
console.info("postLog");
this.postMessage({ type: "log", data: { level: level, msg: msg }});
},
untarBuffer: function(arrayBuffer) {
try {
var tarFileStream = new UntarFileStream(arrayBuffer);
while (tarFileStream.hasNext()) {
var file = tarFileStream.next();
this.postMessage({ type: "extract", data: file }, [file.buffer]);
}
this.postMessage({ type: "complete" });
} catch (err) {
this.postError(err);
}
},
postMessage: function(msg, transfers) {
console.info("postMessage(" + msg + ", " + JSON.stringify(transfers) + ")");
self.postMessage(msg, transfers);
}
};
if (typeof self !== "undefined") {
// We're running in a worker thread
var worker = new UntarWorker();
self.onmessage = function(msg) { worker.onmessage(msg); };
}
function TarFile() {
}
function UntarStream(arrayBuffer) {
this._bufferView = new DataView(arrayBuffer);
this._position = 0;
}
UntarStream.prototype = {
readString: function(charCount) {
//console.log("readString: position " + this.position() + ", " + charCount + " chars");
var charSize = 1;
var byteCount = charCount * charSize;
var charCodes = [];
for (var i = 0; i < charCount; ++i) {
var charCode = this._bufferView.getUint8(this.position() + (i * charSize), true);
if (charCode !== 0) {
charCodes.push(charCode);
} else {
break;
}
}
this.seek(byteCount);
return String.fromCharCode.apply(null, charCodes);
},
readBuffer: function(byteCount) {
var buf;
if (typeof ArrayBuffer.prototype.slice === "function") {
buf = this._bufferView.buffer.slice(this.position(), this.position() + byteCount);
} else {
buf = new ArrayBuffer(byteCount);
var target = new Uint8Array(buf);
var src = new Uint8Array(this._bufferView.buffer, this.position(), byteCount);
target.set(src);
}
this.seek(byteCount);
return buf;
},
seek: function(byteCount) {
this._position += byteCount;
},
peekUint32: function() {
return this._bufferView.getUint32(this.position(), true);
},
position: function(newpos) {
if (newpos === undefined) {
return this._position;
} else {
this._position = newpos;
}
},
size: function() {
return this._bufferView.byteLength;
}
};
function UntarFileStream(arrayBuffer) {
this._stream = new UntarStream(arrayBuffer);
}
UntarFileStream.prototype = {
hasNext: function() {
// A tar file ends with 4 zero bytes
return this._stream.position() + 4 < this._stream.size() && this._stream.peekUint32() !== 0;
},
next: function() {
var stream = this._stream;
var file = new TarFile();
var headerBeginPos = stream.position();
var dataBeginPos = headerBeginPos + 512;
// Read header
file.name = stream.readString(100);
file.mode = stream.readString(8);
file.uid = stream.readString(8);
file.gid = stream.readString(8);
file.size = parseInt(stream.readString(12), 8);
file.modificationTime = parseInt(stream.readString(12), 8);
file.checksum = stream.readString(8);
file.type = stream.readString(1);
file.linkname = stream.readString(1);
file.ustarFormat = stream.readString(6);
if (file.ustarFormat === "ustar") {
file.version = stream.readString(2);
file.uname = stream.readString(32);
file.gname = stream.readString(32);
file.devmajor = stream.readString(8);
file.devminor = stream.readString(8);
file.namePrefix = stream.readString(155);
if (file.namePrefix.length > 0) {
file.name = file.namePrefix + file.name;
}
}
stream.position(dataBeginPos);
// Normal file is either "\0" or 0.
if (file.type === "0" || file.type === "\0") {
file.buffer = stream.readBuffer(file.size);
} else if (file.type == 5) {
// Directory - should we do anything with this? Nope!
} else {
// We only care about real files, not symlinks.
}
if (file.buffer === undefined) {
file.buffer = new ArrayBuffer(0);
}
// File data is padded to reach a 512 byte boundary; skip the padded bytes.
var dataEndPos = dataBeginPos + (file.size > 0 ? file.size + (512 - file.size % 512) : 0);
stream.position(dataEndPos);
return file;
}
};

91
build/dev/untar.js 100644
Wyświetl plik

@ -0,0 +1,91 @@
;(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['ProgressivePromise'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('ProgressivePromise'));
} else {
root.untar = factory(root.ProgressivePromise);
}
}(this, function(ProgressivePromise) {
"use strict";
/* globals window: false, Blob: false, Promise: false, console: false, Worker: false, ProgressivePromise: false */
var workerScriptUri; // Included at compile time
var URL = window.URL || window.webkitURL;
/**
Returns a ProgressivePromise.
*/
function untar(arrayBuffer) {
if (!(arrayBuffer instanceof ArrayBuffer)) {
throw new TypeError("arrayBuffer is not an instance of ArrayBuffer.");
}
if (!window.Worker) {
throw new Error("Worker implementation not available in this environment.");
}
return new ProgressivePromise(function(resolve, reject, progress) {
var worker = new Worker(workerScriptUri);
var files = [];
worker.onerror = function(err) {
reject(err);
};
worker.onmessage = function(message) {
message = message.data;
switch (message.type) {
case "log":
console[message.data.level]("Worker: " + message.data.msg);
break;
case "extract":
var file = decorateExtractedFile(message.data);
files.push(file);
progress(file);
break;
case "complete":
resolve(files);
break;
case "error":
//console.log("error message");
reject(new Error(message.data.message));
break;
default:
reject(new Error("Unknown message from worker: " + message.type));
break;
}
};
//console.info("Sending arraybuffer to worker for extraction.");
worker.postMessage({ type: "extract", buffer: arrayBuffer }, [arrayBuffer]);
});
}
function decorateExtractedFile(file) {
var blob;
var blobUrl;
Object.defineProperties(file, {
blob: {
get: function() {
return blob || (blob = new Blob([this.buffer]));
}
},
getObjectUrl: {
value: function() {
return blobUrl || (blubUrl = URL.createObjectURL(blob));
}
}
});
return file;
}
workerScriptUri = '/base/build/dev/untar-worker.js';
return untar;
}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlcyI6WyJ1bnRhci5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBnbG9iYWxzIHdpbmRvdzogZmFsc2UsIEJsb2I6IGZhbHNlLCBQcm9taXNlOiBmYWxzZSwgY29uc29sZTogZmFsc2UsIFdvcmtlcjogZmFsc2UsIFByb2dyZXNzaXZlUHJvbWlzZTogZmFsc2UgKi9cblxudmFyIHdvcmtlclNjcmlwdFVyaTsgLy8gSW5jbHVkZWQgYXQgY29tcGlsZSB0aW1lXG5cbnZhciBVUkwgPSB3aW5kb3cuVVJMIHx8IHdpbmRvdy53ZWJraXRVUkw7XG5cbi8qKlxuUmV0dXJucyBhIFByb2dyZXNzaXZlUHJvbWlzZS5cbiovXG5mdW5jdGlvbiB1bnRhcihhcnJheUJ1ZmZlcikge1xuXHRpZiAoIShhcnJheUJ1ZmZlciBpbnN0YW5jZW9mIEFycmF5QnVmZmVyKSkge1xuXHRcdHRocm93IG5ldyBUeXBlRXJyb3IoXCJhcnJheUJ1ZmZlciBpcyBub3QgYW4gaW5zdGFuY2Ugb2YgQXJyYXlCdWZmZXIuXCIpO1xuXHR9XG5cblx0aWYgKCF3aW5kb3cuV29ya2VyKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKFwiV29ya2VyIGltcGxlbWVudGF0aW9uIG5vdCBhdmFpbGFibGUgaW4gdGhpcyBlbnZpcm9ubWVudC5cIik7XG5cdH1cblxuXHRyZXR1cm4gbmV3IFByb2dyZXNzaXZlUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLCByZWplY3QsIHByb2dyZXNzKSB7XG5cdFx0dmFyIHdvcmtlciA9IG5ldyBXb3JrZXIod29ya2VyU2NyaXB0VXJpKTtcblxuXHRcdHZhciBmaWxlcyA9IFtdO1xuXG5cdFx0d29ya2VyLm9uZXJyb3IgPSBmdW5jdGlvbihlcnIpIHtcblx0XHRcdHJlamVjdChlcnIpO1xuXHRcdH07XG5cblx0XHR3b3JrZXIub25tZXNzYWdlID0gZnVuY3Rpb24obWVzc2FnZSkge1xuXHRcdFx0bWVzc2FnZSA9IG1lc3NhZ2UuZGF0YTtcblxuXHRcdFx0c3dpdGNoIChtZXNzYWdlLnR5cGUpIHtcblx0XHRcdFx0Y2FzZSBcImxvZ1wiOlxuXHRcdFx0XHRcdGNvbnNvbGVbbWVzc2FnZS5kYXRhLmxldmVsXShcIldvcmtlcjogXCIgKyBtZXNzYWdlLmRhdGEubXNnKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSBcImV4dHJhY3RcIjpcblx0XHRcdFx0XHR2YXIgZmlsZSA9IGRlY29yYXRlRXh0cmFjdGVkRmlsZShtZXNzYWdlLmRhdGEpO1xuXHRcdFx0XHRcdGZpbGVzLnB1c2goZmlsZSk7XG5cdFx0XHRcdFx0cHJvZ3Jlc3MoZmlsZSk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgXCJjb21wbGV0ZVwiOlxuXHRcdFx0XHRcdHJlc29sdmUoZmlsZXMpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIFwiZXJyb3JcIjpcblx0XHRcdFx0XHQvL2NvbnNvbGUubG9nKFwiZXJyb3IgbWVzc2FnZVwiKTtcblx0XHRcdFx0XHRyZWplY3QobmV3IEVycm9yKG1lc3NhZ2UuZGF0YS5tZXNzYWdlKSk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGRlZmF1bHQ6XG5cdFx0XHRcdFx0cmVqZWN0KG5ldyBFcnJvcihcIlVua25vd24gbWVzc2FnZSBmcm9tIHdvcmtlcjogXCIgKyBtZXNzYWdlLnR5cGUpKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHR9O1xuXG5cdFx0Ly9jb25zb2xlLmluZm8oXCJTZW5kaW5nIGFycmF5YnVmZmVyIHRvIHdvcmtlciBmb3IgZXh0cmFjdGlvbi5cIik7XG5cdFx0d29ya2VyLnBvc3RNZXNzYWdlKHsgdHlwZTogXCJleHRyYWN0XCIsIGJ1ZmZlcjogYXJyYXlCdWZmZXIgfSwgW2FycmF5QnVmZmVyXSk7XG5cdH0pO1xufVxuXG5mdW5jdGlvbiBkZWNvcmF0ZUV4dHJhY3RlZEZpbGUoZmlsZSkge1xuXHR2YXIgYmxvYjtcblx0dmFyIGJsb2JVcmw7XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKGZpbGUsIHtcblx0XHRibG9iOiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gYmxvYiB8fCAoYmxvYiA9IG5ldyBCbG9iKFt0aGlzLmJ1ZmZlcl0pKTtcblx0XHRcdH1cblx0XHR9LFxuXHRcdGdldE9iamVjdFVybDoge1xuXHRcdFx0dmFsdWU6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gYmxvYlVybCB8fCAoYmx1YlVybCA9IFVSTC5jcmVhdGVPYmplY3RVUkwoYmxvYikpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSk7XG5cblx0cmV0dXJuIGZpbGU7XG59XG4iXSwiZmlsZSI6InVudGFyLmpzIiwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0=

1
build/dist/untar.js vendored 100644
Wyświetl plik

@ -0,0 +1 @@
!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.untar=t()}(this,function(){"use strict";function e(e){function t(e){for(var t=0,i=r.length;i>t;++t)r[t](e);n.push(e)}if("function"!=typeof Promise)throw new Error("Promise implementation not available in this environment.");var r=[],n=[],i=new Promise(function(r,n){e(r,n,t)});i.progress=function(e){if("function"!=typeof e)throw new Error("cb is not a function.");for(var t=0,o=n.length;o>t;++t)e(n[t]);return r.push(e),i};var o=i.then;return i.then=function(e,t,r){return o.call(i,e,t),void 0!==r&&i.progress(r),i},i}function t(t){if(!(t instanceof ArrayBuffer))throw new TypeError("arrayBuffer is not an instance of ArrayBuffer.");if(!window.Worker)throw new Error("Worker implementation not available in this environment.");return new e(function(e,i,o){var a=new Worker(n),s=[];a.onerror=function(e){i(e)},a.onmessage=function(t){switch(t=t.data,t.type){case"log":console[t.data.level]("Worker: "+t.data.msg);break;case"extract":var n=r(t.data);s.push(n),o(n);break;case"complete":e(s);break;case"error":i(new Error(t.data.message));break;default:i(new Error("Unknown message from worker: "+t.type))}},a.postMessage({type:"extract",buffer:t},[t])})}function r(e){var t,r;return Object.defineProperties(e,{blob:{get:function(){return t||(t=new Blob([this.buffer]))}},getObjectUrl:{value:function(){return r||(blubUrl=i.createObjectURL(t))}}}),e}var n,i=window.URL||window.webkitURL;return n=i.createObjectURL(new Blob(['"use strict";function UntarWorker(){}function TarFile(){}function UntarStream(e){this._bufferView=new DataView(e),this._position=0}function UntarFileStream(e){this._stream=new UntarStream(e)}if(UntarWorker.prototype={onmessage:function(e){try{if("extract"!==e.data.type)throw new Error("Unknown message type: "+e.data.type);this.untarBuffer(e.data.buffer)}catch(t){this.postError(t)}},postError:function(e){this.postMessage({type:"error",data:{message:e.message}})},postLog:function(e,t){console.info("postLog"),this.postMessage({type:"log",data:{level:e,msg:t}})},untarBuffer:function(e){try{for(var t=new UntarFileStream(e);t.hasNext();){var r=t.next();this.postMessage({type:"extract",data:r},[r.buffer])}this.postMessage({type:"complete"})}catch(i){this.postError(i)}},postMessage:function(e,t){console.info("postMessage("+e+", "+JSON.stringify(t)+")"),self.postMessage(e,t)}},"undefined"!=typeof self){var worker=new UntarWorker;self.onmessage=function(e){worker.onmessage(e)}}UntarStream.prototype={readString:function(e){for(var t=1,r=e*t,i=[],n=0;e>n;++n){var s=this._bufferView.getUint8(this.position()+n*t,!0);if(0===s)break;i.push(s)}return this.seek(r),String.fromCharCode.apply(null,i)},readBuffer:function(e){var t;if("function"==typeof ArrayBuffer.prototype.slice)t=this._bufferView.buffer.slice(this.position(),this.position()+e);else{t=new ArrayBuffer(e);var r=new Uint8Array(t),i=new Uint8Array(this._bufferView.buffer,this.position(),e);r.set(i)}return this.seek(e),t},seek:function(e){this._position+=e},peekUint32:function(){return this._bufferView.getUint32(this.position(),!0)},position:function(e){return void 0===e?this._position:void(this._position=e)},size:function(){return this._bufferView.byteLength}},UntarFileStream.prototype={hasNext:function(){return this._stream.position()+4<this._stream.size()&&0!==this._stream.peekUint32()},next:function(){var e=this._stream,t=new TarFile,r=e.position(),i=r+512;t.name=e.readString(100),t.mode=e.readString(8),t.uid=e.readString(8),t.gid=e.readString(8),t.size=parseInt(e.readString(12),8),t.modificationTime=parseInt(e.readString(12),8),t.checksum=e.readString(8),t.type=e.readString(1),t.linkname=e.readString(1),t.ustarFormat=e.readString(6),"ustar"===t.ustarFormat&&(t.version=e.readString(2),t.uname=e.readString(32),t.gname=e.readString(32),t.devmajor=e.readString(8),t.devminor=e.readString(8),t.namePrefix=e.readString(155),t.namePrefix.length>0&&(t.name=t.namePrefix+t.name)),e.position(i),"0"===t.type||"\x00"===t.type?t.buffer=e.readBuffer(t.size):5==t.type,void 0===t.buffer&&(t.buffer=new ArrayBuffer(0));var n=i+(t.size>0?t.size+(512-t.size%512):0);return e.position(n),t}};'])),t});

Wyświetl plik

@ -26,7 +26,6 @@
},
"homepage": "https://github.com/Qvazar/js-untar",
"devDependencies": {
"es6-promise": "^3.0.2",
"gulp": "^3.9.0",
"gulp-add-src": "^0.2.0",
"gulp-concat": "^2.6.0",
@ -42,9 +41,7 @@
"karma-firefox-launcher": "^0.1.6",
"karma-ie-launcher": "^0.2.0",
"karma-jasmine": "^0.3.6",
"karma-phantomjs-launcher": "^0.2.1",
"karma-requirejs": "^0.2.2",
"phantomjs": "^1.9.18",
"requirejs": "^2.1.20"
}
}

Wyświetl plik

@ -56,17 +56,20 @@ function untar(arrayBuffer) {
}
function decorateExtractedFile(file) {
file.blob = new Blob([file.buffer]);
delete file.buffer;
var blob;
var blobUrl;
file.getObjectUrl = function() {
if (!blobUrl) {
blobUrl = URL.createObjectURL(file.blob);
Object.defineProperties(file, {
blob: {
get: function() {
return blob || (blob = new Blob([this.buffer]));
}
},
getObjectUrl: {
value: function() {
return blobUrl || (blubUrl = URL.createObjectURL(blob));
}
}
return blobUrl;
};
});
return file;
}
}