express-busboy
Version:
Busboy for express, mimics the old bodyParser
197 lines (174 loc) • 6.2 kB
JavaScript
/*
* Copyright (c) 2014, Yahoo Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
const busboy = require('connect-busboy');
const key = '@express-busboy';
const path = require('path');
const uuid = require('uuid');
const fs = require('fs');
const mkdirp = require('mkdirp');
const qs = require('qs');
const os = require('os');
const jsonBody = require('body/json');
const crypto = require('crypto');
const fixDups = (item) => {
Object.keys(item).forEach((field) => {
if (Array.isArray(item[field])) {
item[`__${field}__`] = item[field];
item[field] = item[field][0];
}
});
return item;
};
const md5 = (value) => {
return crypto.createHash('md5').update(value).digest('hex');
};
const strip = (value) => {
return md5(value);
};
var convertParams = (item, name, data) => {
if (Array.isArray(item[name])) {
item[name].push(data);
} else if (item[name]) {
item[name] = [item[name], data];
} else {
item[name] = data;
}
};
exports.extend = function(app, options) {
if (app[key]) {
return app;
}
Object.defineProperty(app, key, { value: exports });
options = options || {};
options.immediate = false; //Remove if the user sets it
options.parseBody = typeof options.parseBody === 'boolean' ? options.parseBody : true;
options.path = options.path || path.join(os.tmpdir(), 'express-busboy');
options.strip = options.strip && options.strip instanceof Function ? options.strip : strip;
const restrictMultiple = options.restrictMultiple;
const mimeTypeLimit = options.mimeTypeLimit
? !Array.isArray(options.mimeTypeLimit)
? [options.mimeTypeLimit]
: options.mimeTypeLimit
: null;
delete options.restrictMultiple;
delete options.mimeTypeLimit;
app.use(busboy(options));
app.use((req, res, next) => {
var allowUpload = true;
req.body = req.body || {};
req.files = req.files || {};
if (req.is('json') && req.readable) {
if (!options.parseBody) {
// skip parsing body if parseBody is false
next();
return;
}
jsonBody(req, res, options, (err, body) => {
req.body = body || {};
next();
});
return;
}
if (!req.busboy) {
//Nothing to parse..
return next();
}
if (options.allowedPath) {
allowUpload = false;
/*istanbul ignore else - else case is the default case*/
if (options.allowedPath === req.url) {
allowUpload = true;
} else if (typeof options.allowedPath === 'function') {
allowUpload = !!options.allowedPath(req.url);
} else if (typeof options.allowedPath.test === 'function') {
allowUpload = !!options.allowedPath.test(req.url);
}
}
let bbDone = false;
const finish = () => {
/*istanbul ignore next*/
if (!bbDone) {
return; //bail busboy is still running
}
//Busboy is done, check the files
let complete = true;
Object.keys(req.files).forEach((name) => {
let file = req.files[name];
if (!Array.isArray(file)) {
file = [file];
}
file.forEach((f) => {
if (!f.done) {
//file is not done writing
complete = false;
}
});
});
if (complete) {
//all files are done writing..
next();
}
};
if (options.upload && allowUpload) {
req.busboy.on('file', (name, file, info) => {
const { filename, encoding, mimeType } = info;
const fileUuid = uuid.v4();
const nameStripped = options.strip(name, 'name');
const filenameStripped = options.strip(filename || /*istanbul ignore next*/ '', 'filename');
const out = path.join(options.path, '/', fileUuid, '/', nameStripped, filenameStripped);
if (
mimeTypeLimit &&
!mimeTypeLimit.some((type) => {
return type === mimeType;
})
) {
return file.resume();
}
/*istanbul ignore next*/
if (!filename || filenameStripped === '') {
return file.on('data', () => {});
}
const data = {
uuid: fileUuid,
field: name,
file: out,
filename: filename,
encoding: encoding,
mimetype: mimeType,
truncated: false,
done: false
};
mkdirp.sync(path.dirname(out));
const writer = fs.createWriteStream(out);
writer.on('finish', () => {
data.done = true;
finish();
});
file.pipe(writer);
// Indicate whether the file was truncated
/*istanbul ignore next*/
file.on('limit', () => {
data.truncated = true;
});
convertParams(req.files, name, data);
});
}
req.busboy.on('field', (name, data) => {
convertParams(req.body, name, data);
});
req.busboy.on('close', () => {
req.body = qs.parse(qs.stringify(req.body), { arrayLimit: options.arrayLimit });
if (restrictMultiple) {
[req.body, req.files].forEach(fixDups);
}
bbDone = true;
finish();
});
req.pipe(req.busboy);
});
return app;
};
;