uploadio
Version:
Simple middleware for uploading files.
224 lines (209 loc) • 6.86 kB
JavaScript
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);
};