UNPKG

uploadio

Version:

Simple middleware for uploading files.

224 lines (209 loc) 6.86 kB
const Busboy = require('busboy'); const { onFileSaveToDisk } = require('./saveToDisk'); const path = require('path'); const { buildOptions, parseFileName, debugLog, buildFields, } = require('./utils'); const AWS = require('aws-sdk'); const imageThumbnail = require('image-thumbnail'); const SYNTAX_ERROR_STATUS = 500; const SUCCESS_STATUS = 200; /** * Processes multipart request * Builds a req.body object for fields * Builds a req.files object for files * @param {Object} options expressFileupload and Busboy options * @param {Object} req Express request object * @param {Object} res Express response object * @param {Function} next Express next method * @return {void} */ module.exports = (options, req, res, next) => { // Build busboy options and init busboy instance. const busboyOptions = buildOptions(options, { headers: req.headers, }); let files = []; const busboy = new Busboy(busboyOptions); AWS.config.update({ accessKeyId: options.saveToS3.accessKeyId, secretAccessKey: options.saveToS3.secretAccessKey, }); const S3 = new AWS.S3(); // Build multipart req.body fields busboy.on( 'field', (field, val) => (req.body = buildFields(req.body, field, val)) ); let fileCounter = 0; let fileSize = 0; let hasFiles = false; busboy.on('file', function (fieldname, file, name, encoding, mimetype) { ++fileCounter; file.fileRead = []; const ext = options.keepExtension || name.split('.').pop() ; // name = name.replace('.' + ext, ''); // Check for supportive file types if (options.limits && options.limits.types) { const invalidType = options.limits.types.indexOf(ext) < 0; if (invalidType) { const message = `Unsupported file format, should be ${options.limits.types} but provided ${ext}.`; debugLog(options, message); req.unpipe(busboy); return res.status(SYNTAX_ERROR_STATUS).end(message); } } if (options.persistFileName) { name = name + '.' + ext; } else { const highestLimit = 900000000000000000; const random = Math.floor(Math.random() * highestLimit); name = random + '.' + ext; } const actualFileName = name; if (options.uploadDir) { name = options.uploadDir + '/' + name; } file.on('data', function (chunk) { hasFiles = true; if (options.limits && options.limits.fileSize) { fileSize += chunk.length; if (fileSize >= options.limits.fileSize) { debugLog(options, 'limit reached!'); req.unpipe(busboy); res.status(SYNTAX_ERROR_STATUS).end('Limit Reached'); } } this.fileRead.push(chunk); }); file.on('error', function (err) { debugLog(options, 'Error while buffering the stream'); }); file.on('end', function () { // Concat the chunks into a Buffer const finalBuffer = Buffer.concat(this.fileRead); if (!finalBuffer.length) { req.cloudFiles = []; return next(); } const params = { // your s3 bucket name Bucket: options.saveToS3.bucket, Key: name, // concatinating all chunks Body: finalBuffer, ACL: 'public-read', // optional ContentEncoding: encoding, // required ContentType: mimetype, }; // we are sending buffer data to s3. S3.upload(params, async (err, s3res) => { try { if (err) { debugLog(options, '[Aws Upload Error] ' + err.message); const error = { success: false, error: err.message, message: "Upload error" }; return next(error); } debugLog(options, !err ? '[No error] ' + s3res.Location : 'Error'); const thumbnailImageUrls = []; let fileExt = actualFileName.split('.').pop(); const firstNameOfFile = actualFileName.replace('.' + fileExt, ''); const sizeKb = 1024; const fileObject = { location: s3res.Location, key: s3res.Key, actualFileName: firstNameOfFile, extension: ext, mimetype: mimetype, size: parseFloat( (req.headers['content-length'] / sizeKb).toFixed(5) ), }; if ( !err && s3res && options.thumbnails && options.thumbnails.length ) { debugLog(options, 'Generating thumbnails'); fileObject.thumbnailImageUrls = []; const quality = 90; for (let thumbnail of options.thumbnails) { let fileOptions = { width: thumbnail.width || SUCCESS_STATUS, height: thumbnail.height || SUCCESS_STATUS, responseType: 'buffer', jpegOptions: { force: true, quality, }, ...thumbnail.options }; const thumbnailImg = await imageThumbnail( { uri: s3res.Location, }, fileOptions ); const ext = path.basename(name).split('.').pop(); const fileName = name.replace('.' + ext, ''); const thumbnailKey = fileName + '_' + thumbnail.width + 'x' + thumbnail.height + '.' + ext; const thumbnailParams = { Bucket: options.saveToS3.bucket, Key: thumbnailKey, Body: thumbnailImg, ACL: 'public-read', ContentEncoding: encoding, ContentType: mimetype, }; debugLog(options, 'Generating thumbnails to s3'); let uploadPromise = await new AWS.S3() .putObject(thumbnailParams) .promise(); if (uploadPromise) { fileObject.thumbnailImageUrls.push({ [`${thumbnail.width}x${thumbnail.height}`]: `https://${thumbnailParams.Bucket}.s3.amazonaws.com/${thumbnailParams.Key}`, }); } } } files.push(fileObject); if (fileCounter === files.length) { req.cloudFiles = files; next(); } } catch (error) { next(error); } }); }); }); busboy.on('finish', () => { if (!hasFiles) { req.cloudFiles = []; debugLog(options, 'Busboy finished parsing request.'); next(); } }); busboy.on('error', (err) => { debugLog(options, 'Busboy error'); next(err); }); req.pipe(busboy); };