From 6fe5b307b1a977083f7718064b97c95ea0f95a57 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sat, 4 Mar 2017 22:15:31 +0000 Subject: [PATCH] Allow toBuffer to resolve Promise with info+data #143 --- docs/api-output.md | 10 ++++---- docs/changelog.md | 6 +++++ lib/constructor.js | 1 + lib/output.js | 35 +++++++++++++++++++-------- test/unit/io.js | 60 +++++++++++++++++++++++++++++++++++++++------- 5 files changed, 89 insertions(+), 23 deletions(-) diff --git a/docs/api-output.md b/docs/api-output.md index b5112440..e13cdeab 100644 --- a/docs/api-output.md +++ b/docs/api-output.md @@ -40,15 +40,17 @@ Write output to a Buffer. JPEG, PNG, WebP, and RAW output are supported. By default, the format will match the input image, except GIF and SVG input which become PNG output. -`callback`, if present, gets three arguments `(err, buffer, info)` where: +`callback`, if present, gets three arguments `(err, data, info)` where: -- `err` is an error message, if any. -- `buffer` is the output image data. +- `err` is an error, if any. +- `data` is the output image data. - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`. - A Promises/A+ promise is returned when `callback` is not provided. + A Promise is returned when `callback` is not provided. **Parameters** +- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** + - `options.resolveWithObject` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`. - `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** when no callback is provided diff --git a/docs/changelog.md b/docs/changelog.md index a2afd224..3a8f2733 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,12 @@ Requires libvips v8.4.2. +#### v0.17.3 - TBD + +* Allow toBuffer to optionally resolve a Promise with both info and data. + [#143](https://github.com/lovell/sharp/issues/143) + [@salzhrani](https://github.com/salzhrani) + #### v0.17.2 - 11th February 2017 * Ensure Readable side of Stream can start flowing after Writable side has finished. diff --git a/lib/constructor.js b/lib/constructor.js index 920b5436..dc490751 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -134,6 +134,7 @@ const Sharp = function (input, options) { streamOut: false, withMetadata: false, withMetadataOrientation: -1, + resolveWithObject: false, // output format jpegQuality: 80, jpegProgressive: false, diff --git a/lib/output.js b/lib/output.js index bc65d77f..75ae221a 100644 --- a/lib/output.js +++ b/lib/output.js @@ -48,17 +48,24 @@ const toFile = function toFile (fileOut, callback) { * JPEG, PNG, WebP, and RAW output are supported. * By default, the format will match the input image, except GIF and SVG input which become PNG output. * - * `callback`, if present, gets three arguments `(err, buffer, info)` where: - * - `err` is an error message, if any. - * - `buffer` is the output image data. + * `callback`, if present, gets three arguments `(err, data, info)` where: + * - `err` is an error, if any. + * - `data` is the output image data. * - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`. - * A Promises/A+ promise is returned when `callback` is not provided. + * A Promise is returned when `callback` is not provided. * + * @param {Object} [options] + * @param {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`. * @param {Function} [callback] * @returns {Promise} - when no callback is provided */ -const toBuffer = function toBuffer (callback) { - return this._pipeline(callback); +const toBuffer = function toBuffer (options, callback) { + if (is.object(options)) { + if (is.bool(options.resolveWithObject)) { + this.options.resolveWithObject = options.resolveWithObject; + } + } + return this._pipeline(is.fn(options) ? options : callback); }; /** @@ -423,11 +430,15 @@ const _pipeline = function _pipeline (callback) { return new Promise(function (resolve, reject) { that.on('finish', function () { that._flattenBufferIn(); - sharp.pipeline(that.options, function (err, data) { + sharp.pipeline(that.options, function (err, data, info) { if (err) { reject(err); } else { - resolve(data); + if (that.options.resolveWithObject) { + resolve({ data: data, info: info }); + } else { + resolve(data); + } } }); }); @@ -435,11 +446,15 @@ const _pipeline = function _pipeline (callback) { } else { // output=promise, input=file/buffer return new Promise(function (resolve, reject) { - sharp.pipeline(that.options, function (err, data) { + sharp.pipeline(that.options, function (err, data, info) { if (err) { reject(err); } else { - resolve(data); + if (that.options.resolveWithObject) { + resolve({ data: data, info: info }); + } else { + resolve(data); + } } }); }); diff --git a/test/unit/io.js b/test/unit/io.js index 7806c32c..894848a2 100644 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -77,16 +77,58 @@ describe('Input/output', function () { readable.pipe(pipeline); }); - it('Read from Stream and write to Buffer via Promise', function (done) { - const readable = fs.createReadStream(fixtures.inputJpg); + it('Read from Stream and write to Buffer via Promise resolved with Buffer', function () { const pipeline = sharp().resize(1, 1); - pipeline.toBuffer().then(function (data) { - assert.strictEqual(true, data.length > 0); - done(); - }).catch(function (err) { - throw err; - }); - readable.pipe(pipeline); + fs.createReadStream(fixtures.inputJpg).pipe(pipeline); + return pipeline + .toBuffer({resolveWithObject: false}) + .then(function (data) { + assert.strictEqual(true, data instanceof Buffer); + assert.strictEqual(true, data.length > 0); + }); + }); + + it('Read from Stream and write to Buffer via Promise resolved with Object', function () { + const pipeline = sharp().resize(1, 1); + fs.createReadStream(fixtures.inputJpg).pipe(pipeline); + return pipeline + .toBuffer({resolveWithObject: true}) + .then(function (object) { + assert.strictEqual('object', typeof object); + assert.strictEqual('object', typeof object.info); + assert.strictEqual('jpeg', object.info.format); + assert.strictEqual(1, object.info.width); + assert.strictEqual(1, object.info.height); + assert.strictEqual(3, object.info.channels); + assert.strictEqual(true, object.data instanceof Buffer); + assert.strictEqual(true, object.data.length > 0); + }); + }); + + it('Read from File and write to Buffer via Promise resolved with Buffer', function () { + return sharp(fixtures.inputJpg) + .resize(1, 1) + .toBuffer({resolveWithObject: false}) + .then(function (data) { + assert.strictEqual(true, data instanceof Buffer); + assert.strictEqual(true, data.length > 0); + }); + }); + + it('Read from File and write to Buffer via Promise resolved with Object', function () { + return sharp(fixtures.inputJpg) + .resize(1, 1) + .toBuffer({resolveWithObject: true}) + .then(function (object) { + assert.strictEqual('object', typeof object); + assert.strictEqual('object', typeof object.info); + assert.strictEqual('jpeg', object.info.format); + assert.strictEqual(1, object.info.width); + assert.strictEqual(1, object.info.height); + assert.strictEqual(3, object.info.channels); + assert.strictEqual(true, object.data instanceof Buffer); + assert.strictEqual(true, object.data.length > 0); + }); }); it('Read from Stream and write to Stream', function (done) {