Skip to content

Instantly share code, notes, and snippets.

@58bits
Created August 15, 2020 02:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 58bits/44d6463f3df6d652601c74e63736b693 to your computer and use it in GitHub Desktop.
Save 58bits/44d6463f3df6d652601c74e63736b693 to your computer and use it in GitHub Desktop.
MultiPart Handler
'use strict'
const config = require('../../config')
const { MultipartError } = require('./errors')
const fs = require('fs')
const path = require('path')
const nanoid = require('nanoid')
const Multipart = (request, response, done) => {
// Note: onFile and addToBody cannot be handled on a per reuqest basis at the moment
// https://github.com/fastify/fastify-multipart/issues/129
// https://github.com/fastify/fastify-multipart/issues/131#issuecomment-649345687
if (!request.isMultipart()) {
return response.code(400).header('Connection', 'close').send(new MultipartError('Request is not multipart'))
}
const fields = {}
const files = {}
const mp = request.multipart(() => { }, () => { }, {
limits: {
fileSize: 1024 * 1024 * 8,
files: 10 },
allowed: ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'video/mp4', 'video/webm', 'text/html'],
})
mp.on('field', (fieldname, value) => {
fields[fieldname] = value
})
mp.on('file', (fieldname, file, filename, encoding, mimetype) => {
if (!filename || filename == '') {
file.destroy(new Error('No file provided'))
} else if (!mp.opts.allowed.includes(mimetype)) {
file.destroy(new Error('Not allowed file type'))
} else {
// Generate new filename
filename = nanoid.customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 11)() + path.extname(filename)
// Add file to local list
files[fieldname] = files[fieldname] || []
files[fieldname].push({
filename: filename,
mimetype: mimetype,
extension: path.extname(filename).substring(1),
})
// Write stream to local file
let destination = path.join(config.storage.temp, filename)
var ffs = fs.createWriteStream(destination)
file.pipe(ffs)
}
file.on('limit', () => {
file.destroy(new Error('File size limit reached'))
})
file.on('error', (error) => {
mp.destroy(error)
})
})
mp.on('partsLimit', () => {
mp.destroy(new Error('Maximum number of form parts reached'))
})
mp.on('filesLimit', () => {
mp.destroy(new Error('Maximum number of files reached'))
})
mp.on('fieldsLimit', () => {
mp.destroy(new Error('Maximim number of fields reached'))
})
mp.on('error', (error) => {
for (let i in files) {
for (let j in files[i]) {
let source = path.join(config.storage.temp, files[i][j].filename)
if (fs.statSync(source)) {
fs.unlinkSync(source)
}
}
}
/// TODO: Test - but do not THROW the error here, or Node.js will crash.
const appError = new MultipartError('Error in multipart file upload handler; lib/multipart', error)
return response.code(400).header('Connection', 'close').send(appError)
})
mp.on('finish', () => {
request.body = { ...fields, ...files }
done()
})
}
module.exports = {
Multipart,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment