diff --git a/lib/zip.js/zip.js b/lib/zip.js/zip.js index 04a0e27..18bd748 100644 --- a/lib/zip.js/zip.js +++ b/lib/zip.js/zip.js @@ -42,16 +42,9 @@ define(function(require) { var Inflater = require("zip.js/inflate"); var Deflater = require("zip.js/deflate"); - - var TEXT_PLAIN = "text/plain"; - - var MESSAGE_EVENT = "message"; - var appendABViewSupported; - try { - appendABViewSupported = new Blob([ new DataView(new ArrayBuffer(0)) ]).size === 0; - } catch (e) { - } + var TEXT_PLAIN = "text/plain"; + var MESSAGE_EVENT = "message"; function Crc32() { var crc = -1, that = this; @@ -78,17 +71,6 @@ define(function(require) { return table; })(); - function blobSlice(blob, index, length) { - if (blob.slice) - return blob.slice(index, index + length); - else if (blob.webkitSlice) - return blob.webkitSlice(index, index + length); - else if (blob.mozSlice) - return blob.mozSlice(index, index + length); - else if (blob.msSlice) - return blob.msSlice(index, index + length); - } - function getDataHelper(byteLength, bytes) { var dataBuffer, dataArray; dataBuffer = new ArrayBuffer(byteLength); @@ -103,171 +85,73 @@ define(function(require) { } // Readers - function Reader() { - } - function TextReader(text) { - var that = this, blobReader; - - function init(callback, onerror) { - var blob = new Blob([ text ], { - type : TEXT_PLAIN - }); - blobReader = new BlobReader(blob); - blobReader.init(function() { - that.size = blobReader.size; - callback(); - }, onerror); - } - - function readUint8Array(index, length, callback, onerror) { - blobReader.readUint8Array(index, length, callback, onerror); - } - - that.size = 0; - that.init = init; - that.readUint8Array = readUint8Array; - } - TextReader.prototype = new Reader(); - TextReader.prototype.constructor = TextReader; - - function Uint8Reader(u8) { + // Filer fs-based File Reader, given a path and a fs instance + function FileReader(path, fs) { var that = this; function init(callback, onerror) { - that.size = u8.length; - callback(); + fs.stat(path, function(err, stats) { + if(err) return onerror(err); + + that.size = stats.size; + callback(); + }); + } + + function getData(callback, onerror) { + if(that.data) { + callback(); + return; + } + + fs.readFile(path, function(err, data) { + if(err) return onerror(err); + that.data = data; + callback(); + }); } function readUint8Array(index, length, callback, onerror) { - callback(new Uint8Array(u8.subarray(index, index + length))); + getData(function() { + callback(that.data.subarray(index, index + length)); + }, onerror); } that.size = 0; that.init = init; that.readUint8Array = readUint8Array; } - Uint8Reader.prototype = new Reader(); - Uint8Reader.prototype.constructor = Uint8Reader; + FileReader.prototype.checkCrc32 = false; - function BlobReader(blob) { - var that = this; - - function init(callback) { - this.size = blob.size; - callback(); - } - - function readUint8Array(index, length, callback, onerror) { - var reader = new FileReader(); - reader.onload = function(e) { - callback(new Uint8Array(e.target.result)); - }; - reader.onerror = onerror; - reader.readAsArrayBuffer(blobSlice(blob, index, length)); - } - - that.size = 0; - that.init = init; - that.readUint8Array = readUint8Array; - } - BlobReader.prototype = new Reader(); - BlobReader.prototype.constructor = BlobReader; - - // Writers - - function Writer() { - } - Writer.prototype.getData = function(callback) { - callback(this.data); - }; - - function TextWriter(encoding) { - var that = this, blob; - - function init(callback) { - blob = new Blob([], { - type : TEXT_PLAIN - }); - callback(); - } - - function writeUint8Array(array, callback) { - blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], { - type : TEXT_PLAIN - }); - callback(); - } - - function getData(callback, onerror) { - var reader = new FileReader(); - reader.onload = function(e) { - callback(e.target.result); - }; - reader.onerror = onerror; - reader.readAsText(blob, encoding); - } - - that.init = init; - that.writeUint8Array = writeUint8Array; - that.getData = getData; - } - TextWriter.prototype = new Writer(); - TextWriter.prototype.constructor = TextWriter; - - function Uint8Writer() { - var that = this, u8; + // Filer fs-based File Writer + function FileWriter(path, fs, size) { + var that = this; function init(callback) { - u8 = new Uint8Array(); - callback(); - } - - function writeUint8Array(array, callback) { - var both = new Uint8Array(u8.length + array.length); - both.set(u8); - both.set(array, u8.length); - u8 = both; + // TODO: Overwrite the file. Should we also have a way to fail if exists? + fs.unlink(path, function() { + callback(); + }); } function getData(callback) { - callback(u8); + callback(that.data); } - that.init = init; + function writeUint8Array(array, callback, onerror) { + fs.appendFile(path, array, function(err) { + if(err) return onerror(err); + that.data = array; + callback(); + }); + } + + that.size = size; + that.init = init; that.writeUint8Array = writeUint8Array; that.getData = getData; } - Uint8Writer.prototype = new Writer(); - Uint8Writer.prototype.constructor = Uint8Writer; - - function BlobWriter(contentType) { - var blob, that = this; - - function init(callback) { - blob = new Blob([], { - type : contentType - }); - callback(); - } - - function writeUint8Array(array, callback) { - blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], { - type : contentType - }); - callback(); - } - - function getData(callback) { - callback(blob); - } - - that.init = init; - that.writeUint8Array = writeUint8Array; - that.getData = getData; - } - BlobWriter.prototype = new Writer(); - BlobWriter.prototype.constructor = BlobWriter; // inflate/deflate core functions function launchProcess(process, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) { @@ -309,7 +193,7 @@ define(function(require) { } function inflate(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { - var worker, crc32 = new Crc32(); + var crc32 = new Crc32(); function oninflateappend(sending, array) { if (computeCrc32 && !sending) @@ -321,11 +205,11 @@ define(function(require) { } launchProcess(new Inflater(), reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror); - return worker; + return; } function deflate(reader, writer, level, onend, onprogress, onreaderror, onwriteerror) { - var worker, crc32 = new Crc32(); + var crc32 = new Crc32(); function ondeflateappend(sending, array) { if (sending) @@ -337,7 +221,7 @@ define(function(require) { } launchProcess(new Deflater(), reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror); - return worker; + return; } function copy(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { @@ -472,7 +356,10 @@ define(function(require) { readCommonHeader(that, data, 4, false, onerror); dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength; writer.init(function() { - copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); + if (that.compressionMethod === 0) + copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); + else + inflate(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); }, onwriteerror); }, onreaderror); }; @@ -676,14 +563,8 @@ define(function(require) { } return { - Reader : Reader, - Writer : Writer, - BlobReader : BlobReader, - Uint8Reader : Uint8Reader, - TextReader : TextReader, - BlobWriter : BlobWriter, - Uint8Writer : Uint8Writer, - TextWriter : TextWriter, + FileReader : FileReader, + FileWriter : FileWriter, createReader : function(reader, callback, onerror) { reader.init(function() { callback(createZipReader(reader, onerror)); diff --git a/src/shell/shell.js b/src/shell/shell.js index 1d91bed..0dd50b7 100644 --- a/src/shell/shell.js +++ b/src/shell/shell.js @@ -476,6 +476,44 @@ define(function(require) { request.send(); }; + Shell.prototype.unzip = function(path, options, callback) { + var fs = this.fs; + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + callback = callback || function(){}; + + if(!path) { + callback(new Errors.EINVAL('missing path argument')); + return; + } + + path = Path.resolve(this.cwd, path); + var destination = Path.resolve(options.destination || this.cwd); + + var Zip = require('zip.js/zip'); + Zip.createReader(new Zip.FileReader(path, fs), function(reader) { + reader.getEntries(function(entries) { + + function inflate(entry, callback) { + var path = Path.join(destination, entry.filename); + if(entry.directory) { + this.mkdirp(path, callback); + } else { + entry.getData(new Zip.FileWriter(path, fs, entry.uncompressedSize), function() { callback(); }); + } + } + + async.eachSeries(entries, inflate, function(error) { + if(error) return callback(error); + reader.close(callback); + }); + }); + }, callback); + }; + return Shell; }); diff --git a/tests/spec/shell/unzip.spec.js b/tests/spec/shell/unzip.spec.js new file mode 100644 index 0000000..331a865 --- /dev/null +++ b/tests/spec/shell/unzip.spec.js @@ -0,0 +1,53 @@ +define(["Filer", "util"], function(Filer, util) { + + describe('FileSystemShell.unzip', function() { + beforeEach(util.setup); + afterEach(util.cleanup); + + it('should be a function', function() { + var shell = util.shell(); + expect(shell.unzip).to.be.a('function'); + }); + + it('should fail when path argument is absent', function(done) { + var fs = util.fs(); + var shell = fs.Shell(); + + shell.unzip(null, function(err) { + expect(err).to.exist; + done(); + }); + }); + + it('should download and unzip the contents of a zip file', function(done) { + var fs = util.fs(); + var shell = fs.Shell(); + var url = "test-file.txt.zip"; + var contents = "This is a test file used in some of the tests.\n"; + + fs.writeFile('/original', contents, function(err) { + if(err) throw err; + + shell.wget(url, {filename: url}, function(err, path) { + if(err) throw err; + + shell.unzip(path, function(err) { + if(err) throw err; + + fs.readFile('/original', function(err, originalData) { + if(err) throw err; + + + fs.readFile('/test-file.txt', function(err, data) { + if(err) throw err; + expect(util.typedArrayEqual(data, originalData)).to.be.true; + done(); + }); + }); + }); + }); + }); + }); + + }); +}); diff --git a/tests/test-file.txt.zip b/tests/test-file.txt.zip new file mode 100644 index 0000000..e6bf443 Binary files /dev/null and b/tests/test-file.txt.zip differ diff --git a/tests/test-manifest.js b/tests/test-manifest.js index aa63b50..a5c2a46 100644 --- a/tests/test-manifest.js +++ b/tests/test-manifest.js @@ -58,6 +58,7 @@ define([ "spec/shell/env.spec", "spec/shell/mkdirp.spec", "spec/shell/wget.spec", + "spec/shell/unzip.spec", // Ported node.js tests (filenames match names in https://github.com/joyent/node/tree/master/test) "spec/node-js/simple/test-fs-mkdir",