diff --git a/lib/image-stream/index.js b/lib/image-stream/index.js index d7b84b0a..a838af34 100644 --- a/lib/image-stream/index.js +++ b/lib/image-stream/index.js @@ -67,9 +67,7 @@ const errors = require('../shared/errors'); * }); */ exports.getFromFilePath = (file) => { - return Bluebird.try(() => { - const type = utils.getArchiveMimeType(file); - + return utils.getArchiveMimeType(file).then((type) => { if (!_.has(handlers, type)) { throw errors.createUserError('Invalid image', `The ${type} format is not supported`); } diff --git a/lib/image-stream/utils.js b/lib/image-stream/utils.js index 91c6be52..5f795c5e 100644 --- a/lib/image-stream/utils.js +++ b/lib/image-stream/utils.js @@ -17,7 +17,8 @@ 'use strict'; const _ = require('lodash'); -const readChunk = require('read-chunk'); +const Bluebird = require('bluebird'); +const fs = Bluebird.promisifyAll(require('fs')); const archiveType = require('archive-type'); /** @@ -26,18 +27,34 @@ const archiveType = require('archive-type'); * @public * * @param {String} file - file path - * @returns {String} mime type + * @fulfil {String} - mime type + * @returns {Promise} * * @example - * utils.getArchiveMimeType('path/to/raspberrypi.img.gz'); + * utils.getArchiveMimeType('path/to/raspberrypi.img.gz').then((mimeType) => { + * console.log(mimeType); + * }); */ exports.getArchiveMimeType = (file) => { // `archive-type` only needs the first 261 bytes // See https://github.com/kevva/archive-type - const MAGIC_NUMBER_BUFFER_START = 0; - const MAGIC_NUMBER_BUFFER_END = 261; - const chunk = readChunk.sync(file, MAGIC_NUMBER_BUFFER_START, MAGIC_NUMBER_BUFFER_END); + const ARCHIVE_TYPE_IDENTIFICATION_BYTES_LENGTH = 261; - return _.get(archiveType(chunk), [ 'mime' ], 'application/octet-stream'); + return Bluebird.using(fs.openAsync(file, 'r').disposer((fileDescriptor) => { + return fs.closeAsync(fileDescriptor); + }), (fileDescriptor) => { + const BUFFER_START = 0; + const chunk = new Buffer(ARCHIVE_TYPE_IDENTIFICATION_BYTES_LENGTH); + + return fs.readAsync( + fileDescriptor, + chunk, + BUFFER_START, + ARCHIVE_TYPE_IDENTIFICATION_BYTES_LENGTH, + null + ).then(() => { + return _.get(archiveType(chunk), [ 'mime' ], 'application/octet-stream'); + }); + }); }; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 8e5c81d9..b5ce339c 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -5521,11 +5521,6 @@ "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", "dev": true }, - "read-chunk": { - "version": "2.0.0", - "from": "read-chunk@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.0.0.tgz" - }, "read-only-stream": { "version": "2.0.0", "from": "read-only-stream@>=2.0.0 <3.0.0", diff --git a/package.json b/package.json index 6b78f9a8..c975cddd 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "node-ipc": "^8.9.2", "node-stream-zip": "^1.3.4", "path-is-inside": "^1.0.2", - "read-chunk": "^2.0.0", "redux": "^3.5.2", "redux-localstorage": "^0.4.1", "resin-cli-form": "^1.4.1", diff --git a/tests/image-stream/utils.spec.js b/tests/image-stream/utils.spec.js index 54a312cf..bf1c519d 100644 --- a/tests/image-stream/utils.spec.js +++ b/tests/image-stream/utils.spec.js @@ -25,29 +25,44 @@ describe('ImageStream: Utils', function() { describe('.getArchiveMimeType()', function() { - it('should return application/x-bzip2 for a bz2 archive', function() { + it('should resolve application/x-bzip2 for a bz2 archive', function(done) { const file = path.join(DATA_PATH, 'bz2', 'raspberrypi.img.bz2'); - m.chai.expect(utils.getArchiveMimeType(file)).to.equal('application/x-bzip2'); + utils.getArchiveMimeType(file).then((type) => { + m.chai.expect(type).to.equal('application/x-bzip2'); + done(); + }).catch(done); }); - it('should return application/x-xz for a xz archive', function() { + it('should resolve application/x-xz for a xz archive', function(done) { const file = path.join(DATA_PATH, 'xz', 'raspberrypi.img.xz'); - m.chai.expect(utils.getArchiveMimeType(file)).to.equal('application/x-xz'); + utils.getArchiveMimeType(file).then((type) => { + m.chai.expect(type).to.equal('application/x-xz'); + done(); + }).catch(done); }); - it('should return application/gzip for a gz archive', function() { + it('should resolve application/gzip for a gz archive', function(done) { const file = path.join(DATA_PATH, 'gz', 'raspberrypi.img.gz'); - m.chai.expect(utils.getArchiveMimeType(file)).to.equal('application/gzip'); + utils.getArchiveMimeType(file).then((type) => { + m.chai.expect(type).to.equal('application/gzip'); + done(); + }).catch(done); }); - it('should return application/zip for a zip archive', function() { + it('should resolve application/zip for a zip archive', function(done) { const file = path.join(DATA_PATH, 'zip', 'zip-directory-rpi-only.zip'); - m.chai.expect(utils.getArchiveMimeType(file)).to.equal('application/zip'); + utils.getArchiveMimeType(file).then((type) => { + m.chai.expect(type).to.equal('application/zip'); + done(); + }).catch(done); }); - it('should return application/octet-stream for an uncompress image', function() { + it('should resolve application/octet-stream for an uncompress image', function(done) { const file = path.join(DATA_PATH, 'images', 'raspberrypi.img'); - m.chai.expect(utils.getArchiveMimeType(file)).to.equal('application/octet-stream'); + utils.getArchiveMimeType(file).then((type) => { + m.chai.expect(type).to.equal('application/octet-stream'); + done(); + }).catch(done); }); });