UNPKG

@dreesq/serpent

Version:

An express wrapper for developing fast web applications

177 lines (140 loc) 4.48 kB
const fs = require('fs'); const qs = require('qs'); const {APP_PATH} = require('../constants'); const path = require('path'); const busboy = require('connect-busboy'); const {promisify} = require('util'); const { v4: uuid } = require('uuid'); /** * Promisify functions */ const unlink = promisify(fs.unlink); const rename = promisify(fs.rename); /** * Helper function * @param item * @param name * @param data */ const addData = (item, name, data) => { if (Array.isArray(item[name])) { item[name].push(data); return; } if (item[name]) { item[name] = [item[name], data]; return; } item[name] = data; }; /** * Convert to bytes * @param text * @returns {number} */ const unhumanize = text =>{ const powers = {'k': 1, 'm': 2, 'g': 3, 't': 4}; const regex = /(\d+(?:\.\d+)?)\s?(k|m|g|t)?b?/i; const res = regex.exec(text); return res[1] * Math.pow(1024, powers[res[2].toLowerCase()]); }; /** * Upload middleware * @param options * @returns {Function} */ module.exports = options => { return (req, res, next) => { const middleware = (error) => { if (error) { return; } if (!req.busboy) { return next(); } let done = false; req.body = req.body || {}; req.files = req.files || {}; const finish = async () => { if (!done) { return; } let complete = true; for (const name in req.files) { let files = req.files[name]; if (!Array.isArray(files)) { files = [files]; } for (const file of files) { if (!file.done) { complete = false; continue; } if (file.invalid) { await unlink(file.path); } } } if (complete) { next(); } }; req.busboy.on('file', (name, file, filename, encoding, mime) => { const ext = filename.substr(filename.lastIndexOf('.') + 1); const fileName = `${uuid()}.${ext}`; const basePath = path.join(APP_PATH, options.basePath, '/'); const out = path.join(basePath, fileName); const extensions = Array.isArray(options.ext) ? options.ext : [options.ext]; if (extensions.indexOf(name) === -1) { return file.resume(); } if (!filename || filename === '') { return file.on('data', () => { }); } const data = { field: name, path: out, filename: fileName, encoding, mimetype: mime, truncated: false, done: false, isFile: true, async delete() { await unlink(out); }, async rename(name) { await rename(out, `${basePath}/${name}`) } }; const writer = fs.createWriteStream(out); writer.on('finish', async () => { data.done = true; await finish(); }); file.pipe(writer); file.on('limit', () => { data.invalid = true; }); addData(req.files, name, data); }); req.busboy.on('field', (name, data) => { addData(req.body, name, data); }); req.busboy.on('finish', async () => { req.body = qs.parse(qs.stringify(req.body)); done = true; await finish(); }); req.pipe(req.busboy); }; const opts = { limits: { files: options.files || 1, fileSize: unhumanize(options.maxSize || '10 mb') } }; return busboy(opts)(req, res, middleware); } };