diff --git a/.gitignore b/.gitignore index 9fba93d..ab6c460 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ coverage # tmp folder tmp +.tmp # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release @@ -27,5 +28,6 @@ node_modules # Users Environment Variables .lock-wscript -*.jpg -!test/test.jpg +test.jpg + +.github diff --git a/README.md b/README.md index 96ddfeb..8fdc31b 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,20 @@ Download images from remote URLs or use local images and encode/decode them to B `npm install node-base64-image --save` ### Usage -`var base64 = require('node-base64-image');` +`var base64 = require('node-base64-image'); // ES5` + +`import {encode, decode} from 'node-base64-image'; // ES6` + +### Documentation +Read the documentation in [DOCUMENTATION](docs/docs.md). + +### Contributing +Read the [CONTRIBUTING](CONTRIBUTING.md) guide for information. ### License -Licensed under MIT. +Licensed under MIT. See [LICENSE](LICENSE) for more information. ### Issues -Report a bug in the issues. +Report a bug in issues. Made with love in Dhaka, Bangladesh by [Riyadh Al Nur](https://verticalaxisbd.com) diff --git a/docs/docs.md b/docs/docs.md new file mode 100644 index 0000000..c5186a1 --- /dev/null +++ b/docs/docs.md @@ -0,0 +1,49 @@ +# fnCallback + +[src/node-base64-image.js:13-13](https://github.com/riyadhalnur/node-base64-image/blob/48ccb3a785ba53841dc0821548dff8177da7bfb0/src/node-base64-image.js#L13-L13 "Source code on GitHub") + +Callback for encode/decode functions + +**Parameters** + +- `Error` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** object +- `Response` **([string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** string or Buffer object + +# encode + +[src/node-base64-image.js:27-65](https://github.com/riyadhalnur/node-base64-image/blob/48ccb3a785ba53841dc0821548dff8177da7bfb0/src/node-base64-image.js#L27-L65 "Source code on GitHub") + +Encodes a remote or local image to Base64 encoded string or Buffer + +**Parameters** + +- `url` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** URL of remote image or local path to image +- `options` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Options object for extra configuration (optional, default `{}`) + - `options.string` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Returns a Base64 encoded string. Defaults to Buffer object + - `options.local` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Encode a local image file instead of a remote image +- `callback` **fnCallback** Callback function + +Returns **fnCallback** Returns the callback + +# result + +[src/node-base64-image.js:41-41](https://github.com/riyadhalnur/node-base64-image/blob/48ccb3a785ba53841dc0821548dff8177da7bfb0/src/node-base64-image.js#L41-L41 "Source code on GitHub") + +# result + +[src/node-base64-image.js:58-58](https://github.com/riyadhalnur/node-base64-image/blob/48ccb3a785ba53841dc0821548dff8177da7bfb0/src/node-base64-image.js#L58-L58 "Source code on GitHub") + +# decode + +[src/node-base64-image.js:77-89](https://github.com/riyadhalnur/node-base64-image/blob/48ccb3a785ba53841dc0821548dff8177da7bfb0/src/node-base64-image.js#L77-L89 "Source code on GitHub") + +Decodes an base64 encoded image buffer and saves it to disk + +**Parameters** + +- `imageBuffer` **[Buffer](https://nodejs.org/api/buffer.html)** Image Buffer object +- `options` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Options object for extra configuration (optional, default `{}`) + - `options.filename` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Filename for the final image file +- `callback` **fnCallback** Callback function + +Returns **fnCallback** Returns the callback diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 2f7a74e..ed7c068 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -135,11 +135,19 @@ function typeCheck(done) { } function gitTag() { + _registerBabel(); return gulp.src(['./package.json']) .pipe($.tagVersion()); } -const watchFiles = ['src/**/*', 'test/**/*', 'package.json', '**/.eslintrc', '.jscsrc']; +function generateDocs() { + _registerBabel(); + return gulp.src('src/node-base64-image.js') + .pipe($.documentation({ format: 'md', filename: 'docs.md' })) + .pipe(gulp.dest('docs')); +} + +const watchFiles = ['src/**/*', 'test/**/*', 'package.json', '**/.eslintrc']; // Run the headless unit tests as you make changes. function watch() { @@ -225,6 +233,9 @@ gulp.task('flow', typeCheck); // Tag with version in package.json gulp.task('tag', gitTag); +// Generate documentation +gulp.task('doc', generateDocs); + // Set up a livereload environment for our spec runner `test/runner.html` gulp.task('test-browser', ['lint', 'clean-tmp'], testBrowser); diff --git a/package.json b/package.json index c28d05f..9280364 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "build": "gulp build", "coverage": "gulp coverage", "tag": "gulp tag", - "prepublish": "npm run build && npm test && npm run tag" + "doc": "gulp doc", + "prepublish": "npm run build && npm run coverage && npm run tag" }, "repository": { "type": "git", @@ -49,6 +50,7 @@ "glob": "^6.0.3", "gulp": "^3.9.0", "gulp-babel": "^6.1.1", + "gulp-documentation": "2.2.0", "gulp-eslint": "^2.0.0", "gulp-filter": "^3.0.0", "gulp-istanbul": "^0.10.3", diff --git a/src/node-base64-image.js b/src/node-base64-image.js index 3a5459b..8f7e6f9 100644 --- a/src/node-base64-image.js +++ b/src/node-base64-image.js @@ -8,12 +8,14 @@ import {readFile as read, writeFile as write} from 'fs'; * * @callback fnCallback * @param {Object} Error object - * @param {string} Response message + * @param {(string|Object)} Response string or Buffer object */ type Callback = (err: ?Error, x?: T) => void; /** * Encodes a remote or local image to Base64 encoded string or Buffer + * + * @name encode * @param {string} url - URL of remote image or local path to image * @param {Object} [options={}] - Options object for extra configuration * @param {boolean} options.string - Returns a Base64 encoded string. Defaults to Buffer object @@ -22,26 +24,66 @@ type Callback = (err: ?Error, x?: T) => void; * @todo Option to wrap string every 76 characters for strings larger than 76 characters * @return {fnCallback} - Returns the callback */ -export function encode(url: string, options: Object = {}, callback: Callback) { +export function encode(url: string, options: Object = {string: false, local: false}, callback: Callback) { // eslint-disable-line if (_.isUndefined(url) || _.isNull(url) || !_.isString(url)) { return callback(new Error('URL is undefined or not properly formatted')); } - if (!_.isFunction(callback)) { - return callback(new Error('Callback needs to be a function')); + if (options.local) { + read(url, (err, body) => { + if (err) { + return callback(err); + } + + /** + * @todo Handle this better. + */ + let result = options.string ? body.toString('base64') : new Buffer(body, 'base64'); + return callback(null, result); + }); + } else { + request({ url: url, encoding: null }, (err, response, body) => { + if (err) { + return callback(err); + } + + if (!body) { + return callback(new Error('Error retrieving image - Empty Body!')); + } + + if (body && response.statusCode === 200) { + /** + * @todo Handle this better. + */ + let result = options.string ? body.toString('base64') : new Buffer(body, 'base64'); + return callback(null, result); + } + + return callback(new Error('Error retrieving image - Status Code ' + response.statusCode)); + }); } } /** * Decodes an base64 encoded image buffer and saves it to disk + * + * @name decode * @param {Buffer} imageBuffer - Image Buffer object * @param {Object} [options={}] - Options object for extra configuration * @param {string} options.filename - Filename for the final image file * @param {fnCallback} callback - Callback function * @return {fnCallback} - Returns the callback */ -export function decode(imageBuffer: any, options: Object = {}, callback: Callback) { +export function decode(imageBuffer: any, options: Object = {filename: 'saved-image'}, callback: Callback) { // eslint-disable-line if (!_.isBuffer(imageBuffer)) { return callback(new Error('The image is not a Buffer object type')); } + + write(options.filename + '.jpg', imageBuffer, 'base64', (err) => { + if (err) { + return callback(err); + } + + return callback(null, 'Image saved successfully to disk!'); + }); } diff --git a/test/setup/.globals.json b/test/setup/.globals.json index ae33857..8a30f4e 100644 --- a/test/setup/.globals.json +++ b/test/setup/.globals.json @@ -8,6 +8,7 @@ "stub": true, "useFakeServer": true, "useFakeTimers": true, - "useFakeXMLHttpRequest": true + "useFakeXMLHttpRequest": true, + "timeout": 15000 } } diff --git a/test/unit/node-base64-image.js b/test/unit/node-base64-image.js index 543b9e2..a48ac98 100644 --- a/test/unit/node-base64-image.js +++ b/test/unit/node-base64-image.js @@ -1,8 +1,140 @@ import {encode, decode} from '../../src/node-base64-image.js'; -describe('Base64 Image', () => { +describe('Base64 Image decode/encode', () => { it('should exist', () => { should.exist(encode); should.exist(decode); }); + + describe('Encoder', () => { + it('should return an error if callback is not a function', (done) => { + let url; + let options = {}; + let callback; + + (function () { + encode(url, options, callback); + }).should.throw(TypeError); + + done(); + }); + + it('should return an error if url is null or undefined', (done) => { + let url; + let options = {}; + + encode(url, options, (err, image) => { + err.should.exist; + err.message.should.equal('URL is undefined or not properly formatted'); + + should.not.exist(image); + done(); + }); + }); + + it('should return an error if local image could not be loaded', (done) => { + let url = __dirname + '/noimage.jpg'; + let options = {local: true}; + + encode(url, options, (err, image) => { + err.should.exist; + + should.not.exist(image); + done(); + }); + }); + + it('should return an error if remote image could not be loaded', (done) => { + let url = 'https://verticalaxisbd.com/noimage.jpg'; + let options = {}; + + encode(url, options, (err, image) => { + err.should.exist; + err.message.should.equal('Error retrieving image - Status Code 404'); + + should.not.exist(image); + done(); + }); + }); + + it('should download an image and return the Base64 encoded string', function (done) { + let url = 'https://res.cloudinary.com/verticalaxisbd/image/upload/h_239,w_239/rg1kxkgxayhdgoqdaejz.jpg'; // eslint-disable-line + let options = {string: true}; + + encode(url, options, (err, image) => { + should.not.exist(err); + + image.should.exist; + image.should.match(/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/); + done(); + }); + }); + + it('should download an image and return the Buffer object by default', function (done) { + let url = 'https://res.cloudinary.com/verticalaxisbd/image/upload/h_239,w_239/rg1kxkgxayhdgoqdaejz.jpg'; // eslint-disable-line + let options = {}; + + encode(url, options, (err, image) => { + should.not.exist(err); + + image.should.exist; + image.should.be.an.instanceOf(Buffer); + done(); + }); + }); + + it('should encode local image and return the Buffer object by default', (done) => { + let path = __dirname + '/test.jpg'; + let options = {local: true}; + + encode(path, options, (err, image) => { + should.not.exist(err); + + image.should.exist; + image.should.be.an.instanceOf(Buffer); + done(); + }); + }); + + it('should encode local image and return the Base64 encoded string', (done) => { + let path = __dirname + '/test.jpg'; + let options = {local: true, string: true}; + + encode(path, options, (err, image) => { + should.not.exist(err); + + image.should.exist; + image.should.match(/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/); + done(); + }); + }); + }); + + describe('Decoder', () => { + it('should return an error if image is not a Buffer object', (done) => { + let imageData; + let options = {}; + + decode(imageData, options, (err, response) => { + err.should.exist; + err.message.should.equal('The image is not a Buffer object type'); + + should.not.exist(response); + done(); + }); + }); + + it('should decode a Base64 encoded image and save it to disk', (done) => { + let options = {filename: 'test'}; + let imageData = new Buffer('/9j/4AAQSkZJRgABAQAAAQABAAD/2w//Z', 'base64'); + + decode(imageData, options, (err, response) => { + should.not.exist(err); + + response.should.exist; + response.should.equal('Image saved successfully to disk!'); + done(); + }); + }); + }); }); diff --git a/test/unit/test.jpg b/test/unit/test.jpg new file mode 100644 index 0000000..9ac3bba Binary files /dev/null and b/test/unit/test.jpg differ