From 0598fe097d8ec53cad4e6409c4c16ac1c16f9bf6 Mon Sep 17 00:00:00 2001 From: ikhsanalatsary Date: Thu, 2 May 2019 02:44:46 +0700 Subject: [PATCH] feat(multer-sharp): support upload array and fix- still upload when transform error --- index.js | 59 +++++++++------ test/implementation.test.js | 140 +++++++++++++++++++++++++++++------- 2 files changed, 152 insertions(+), 47 deletions(-) diff --git a/index.js b/index.js index 2553c75..193d60e 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,10 @@ +/* eslint-disable func-names */ + 'use strict'; const gcloud = require('@google-cloud/storage'); const Promise = require('bluebird'); +const { PassThrough } = require('stream'); const defaultOptions = require('./config/default'); const sharpOptions = require('./lib/get-sharp-options'); @@ -16,8 +19,7 @@ class MulterSharp { /* eslint-disable no-param-reassign, no-underscore-dangle */ options.bucket = options.bucket || process.env.GCS_BUCKET || null; options.projectId = options.projectId || process.env.GC_PROJECT || null; - options.keyFilename = - options.keyFilename || process.env.GCS_KEYFILE || null; + options.keyFilename = options.keyFilename || process.env.GCS_KEYFILE || null; this._check(options); @@ -58,14 +60,24 @@ class MulterSharp { const filenameWithSuffix = `${filename}-${size.suffix}`; const gcNameBySuffix = `${gcName}-${size.suffix}`; const gcFile = this.gcsBucket.file(gcNameBySuffix); + const streamCopy = new PassThrough(); const resizerStream = transformer(this.sharpOptions, size); const writableStream = gcFile.createWriteStream(fileOptions); + stream.pipe(streamCopy); return new Promise((resolve, reject) => { + streamCopy.pipe(resizerStream).pipe(writableStream); resizerStream.on('info', logger); - resizerStream.on('error', reject); + resizerStream.on('error', function (transformErr) { + resizerStream.unpipe(writableStream); + this.end(); + reject(transformErr); + }); - writableStream.on('error', reject); + writableStream.on('error', function (gcErr) { + this.end(); + reject(gcErr); + }); writableStream.on('finish', () => { const uri = encodeURI( `https://storage.googleapis.com/${ @@ -79,8 +91,6 @@ class MulterSharp { suffix: size.suffix }); }); - - stream.pipe(resizerStream).pipe(writableStream); }); }; } @@ -97,17 +107,16 @@ class MulterSharp { }), gzip: this.options.gzip }; - const gcName = - typeof destination === 'string' && destination.length > 0 - ? `${destination}/${filename}` - : filename; + const gcName = typeof destination === 'string' && destination.length > 0 + ? `${destination}/${filename}` + : filename; const gcFile = this.gcsBucket.file(gcName); - const stream = file.stream; + const { stream } = file; const resizerStream = transformer(this.sharpOptions, this.options.size); const writableStream = gcFile.createWriteStream(fileOptions); if ( - Array.isArray(this.options.sizes) && - this.options.sizes.length > 0 + Array.isArray(this.options.sizes) + && this.options.sizes.length > 0 ) { this._uploadWithMultipleSize( this.options.sizes, @@ -136,10 +145,9 @@ class MulterSharp { _removeFile(req, file, cb) { this.getDestination(req, file, (destErr, destination) => { if (destErr) cb(destErr); - const gcName = - typeof destination === 'string' && destination.length > 0 - ? `${destination}/${file.filename}` - : file.filename; + const gcName = typeof destination === 'string' && destination.length > 0 + ? `${destination}/${file.filename}` + : file.filename; const gcFile = this.gcsBucket.file(gcName); gcFile.delete(cb); }); @@ -154,12 +162,19 @@ class MulterSharp { writableStream, cb ) { - stream - .pipe(resizerStream) + stream.pipe(resizerStream).pipe(writableStream); + resizerStream .on('info', logger) - .on('error', (transformErr) => cb(transformErr)) - .pipe(writableStream) - .on('error', (gcErr) => cb(gcErr)) + .on('error', function (transformErr) { + resizerStream.unpipe(writableStream); + cb(transformErr); + this.end(); + }); + writableStream + .on('error', function (gcErr) { + cb(gcErr); + this.end(); + }) .on('finish', () => { const uri = encodeURI( `https://storage.googleapis.com/${this.options.bucket}/${gcName}` diff --git a/test/implementation.test.js b/test/implementation.test.js index 6e527a7..3ccb4ca 100644 --- a/test/implementation.test.js +++ b/test/implementation.test.js @@ -10,6 +10,7 @@ const multerSharp = require('../index'); const config = require('./config'); const app = express(); +const agent = supertest.agent(app); const wrongConfig = { uploads: { gcsUpload: { @@ -80,12 +81,16 @@ const storage4 = multerSharp({ destination: config.uploads.gcsUpload.destination, size: { width: 200 }, crop: 16, // crop strategy - background: { r: 0, g: 0, b: 100, alpha: 0 }, + background: { + r: 0, g: 0, b: 100, alpha: 0 + }, withoutEnlargement: true, ignoreAspectRatio: true, trim: 50, flatten: true, - extend: { top: 10, bottom: 20, left: 10, right: 10 }, + extend: { + top: 10, bottom: 20, left: 10, right: 10 + }, negate: true, rotate: 90, flip: true, @@ -113,16 +118,22 @@ const storage5 = multerSharp({ destination: config.uploads.gcsUpload.destination, size: { width: 400, height: 400 }, crop: 'north', - background: { r: 0, g: 0, b: 0, alpha: 0 }, + background: { + r: 0, g: 0, b: 0, alpha: 0 + }, embed: true, max: true, min: true, withoutEnlargement: true, ignoreAspectRatio: true, - extract: { left: 0, top: 2, width: 50, height: 100 }, + extract: { + left: 0, top: 2, width: 50, height: 100 + }, trim: 50, flatten: true, - extend: { top: 10, bottom: 20, left: 10, right: 10 }, + extend: { + top: 10, bottom: 20, left: 10, right: 10 + }, negate: true, rotate: 90, flip: true, @@ -156,7 +167,9 @@ const storage6 = multerSharp({ height: 400 }, max: true, - extract: { left: 0, top: 2, width: 400, height: 400 } + extract: { + left: 0, top: 2, width: 400, height: 400 + } }); const upload6 = multer({ storage: storage6 }); @@ -182,6 +195,36 @@ const storage8 = multerSharp({ }); const upload8 = multer({ storage: storage8 }); +const storage11 = multerSharp({ + bucket: config.uploads.gcsUpload.bucket, + projectId: config.uploads.gcsUpload.projectId, + keyFilename: config.uploads.gcsUpload.keyFilename, + acl: config.uploads.gcsUpload.acl, + destination: 'uploadMultiArray', + sizes: [ + // { suffix: 'xlg', width: 1200, height: 1200 }, + // { suffix: 'lg', width: 800, height: 800 }, + { suffix: 'md', width: 500, height: 500 }, + { suffix: 'sm', width: 300, height: 300 }, + { suffix: 'xs', width: 100, height: 100 } + ] +}); +const upload11 = multer({ storage: storage11 }); + +const storage12 = multerSharp({ + bucket: config.uploads.gcsUpload.bucket, + projectId: config.uploads.gcsUpload.projectId, + keyFilename: config.uploads.gcsUpload.keyFilename, + acl: config.uploads.gcsUpload.acl, + destination: 'uploadArray', + size: { + width: 400, + height: 400 + }, + max: true +}); +const upload12 = multer({ storage: storage12 }); + const storage9 = multerSharp({ bucket: wrongConfig.uploads.gcsUpload.bucket, projectId: wrongConfig.uploads.gcsUpload.projectId, @@ -208,7 +251,9 @@ const storage10 = multerSharp({ { suffix: 'sm', width: 300, height: 300 }, { suffix: 'xs', width: 100, height: 100 } ], - extract: { left: 0, top: 2, width: 400, height: 400 } + extract: { + left: 0, top: 2, width: 400, height: 400 + } }); const upload10 = multer({ storage: storage10 }); @@ -287,6 +332,26 @@ app.post('/uploadwithmultiplesize', upload8.single('myPic'), (req, res, next) => next(); }); +app.post('/uploadarraywithmultiplesize', upload11.array('myPic', 2), (req, res, next) => { + lastReq = req; + lastRes = res; + + if (lastReq && lastReq.files) { + res.sendStatus(200); + } + next(); +}); + +app.post('/uploadarray', upload12.array('myPic', 2), (req, res, next) => { + lastReq = req; + lastRes = res; + + if (lastReq && lastReq.files) { + res.sendStatus(200); + } + next(); +}); + app.post('/uploadwithmultiplesizetransformerror', (req, res) => { const uploadAndError = upload9.single('myPic'); uploadAndError(req, res, (uploadError) => { @@ -304,22 +369,22 @@ app.post('/uploadwithmultiplesizegcerror', upload10.single('myPic'), (req, res) describe('express', () => { // this.timeout(15000); it('initial server', (done) => { - supertest(app) + agent .get('/book') .expect(200, done); }); it('successfully uploads a file', (done) => { - supertest(app) + agent .post('/upload') .attach('myPic', 'test/nodejs-512.png') .expect(200, done); }); it('returns a req.file with the Google Cloud Storage filename and path', (done) => { - supertest(app) + agent .post('/upload') .attach('myPic', 'test/nodejs-512.png') .end(() => { - const file = lastReq.file; + const { file } = lastReq; console.log(file); expect(file).toHaveProperty('path'); expect(file).toHaveProperty('filename'); @@ -333,11 +398,11 @@ describe('express', () => { }); }); it('return a req.file with the optional filename', (done) => { - supertest(app) + agent .post('/uploadwithfilename') .attach('myPic', 'test/nodejs-512.png') .end(() => { - const file = lastReq.file; + const { file } = lastReq; console.log(file); expect(file).toHaveProperty('path'); expect(file).toHaveProperty('filename'); @@ -352,11 +417,11 @@ describe('express', () => { }); }); it('return a req.file with the optional destination', (done) => { - supertest(app) + agent .post('/uploadwithdestination') .attach('myPic', 'test/nodejs-512.png') .end(() => { - const file = lastReq.file; + const { file } = lastReq; console.log(file); expect(file).toHaveProperty('path'); expect(file).toHaveProperty('filename'); @@ -371,11 +436,11 @@ describe('express', () => { }); }); it('return a req.file with mimetype image/jpeg', (done) => { - supertest(app) + agent .post('/uploadconverttojpeg') .attach('myPic', 'test/nodejs-512.png') .end(() => { - const file = lastReq.file; + const { file } = lastReq; console.log(file); expect(file).toHaveProperty('path'); expect(file).toHaveProperty('filename'); @@ -391,13 +456,13 @@ describe('express', () => { }); }); it('upload and delete after', (done) => { - supertest(app) + agent .post('/uploadanddelete') .attach('myPic', 'test/nodejs-512.png') .expect(200, done); }); it('upload and return error, cause transform/resize error', (done) => { - supertest(app) + agent .post('/uploadwithtransformerror') .attach('myPic', 'test/nodejs-512.png') .end((err, res) => { @@ -407,7 +472,7 @@ describe('express', () => { }); }); it('upload and return error, cause google cloud error', (done) => { - supertest(app) + agent .post('/uploadwithgcserror') .attach('myPic', 'test/nodejs-512.png') .end((err, res) => { @@ -418,12 +483,11 @@ describe('express', () => { }); it('return a req.file with multiple sizes', (done) => { // jest.setTimeout(done, 1000); - supertest(app) + agent .post('/uploadwithmultiplesize') .attach('myPic', 'test/nodejs-512.png') .end(() => { - const file = lastReq.file; - console.log(file); + const { file } = lastReq; expect(file).toHaveProperty('md'); expect(file).toHaveProperty('sm'); expect(file).toHaveProperty('xs'); @@ -434,8 +498,34 @@ describe('express', () => { done(); }); }); + it('return a req.files', (done) => { + // jest.setTimeout(done, 1000); + agent + .post('/uploadarray') + .attach('myPic', 'test/nodejs-512.png') + .attach('myPic', 'test/nodejs-512.png') + .end(() => { + const file = lastReq.files; + console.log('files 1 ', file); + expect(file).toHaveLength(2); + done(); + }); + }); + it('return a req.files with multiple sizes', (done) => { + // jest.setTimeout(done, 1000); + agent + .post('/uploadarraywithmultiplesize') + .attach('myPic', 'test/nodejs-512.png') + .attach('myPic', 'test/nodejs-512.png') + .end(() => { + const file = lastReq.files; + console.log('files 2 ', file); + expect(file).toHaveLength(2); + done(); + }); + }); it('upload multisize and return error, cause transform/resize error', (done) => { - supertest(app) + agent .post('/uploadwithmultiplesizetransformerror') .attach('myPic', 'test/nodejs-512.png') .end((err, res) => { @@ -445,7 +535,7 @@ describe('express', () => { }); }); it('upload multisize and return error, cause google cloud error', (done) => { - supertest(app) + agent .post('/uploadwithmultiplesizegcerror') .attach('myPic', 'test/nodejs-512.png') .end((err, res) => {