You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					182 lines
				
				5.2 KiB
			
		
		
			
		
	
	
					182 lines
				
				5.2 KiB
			| 
											3 years ago
										 | var is = require('type-is') | ||
|  | var Busboy = require('busboy') | ||
|  | var extend = require('xtend') | ||
|  | var onFinished = require('on-finished') | ||
|  | var appendField = require('append-field') | ||
|  | 
 | ||
|  | var Counter = require('./counter') | ||
|  | var MulterError = require('./multer-error') | ||
|  | var FileAppender = require('./file-appender') | ||
|  | var removeUploadedFiles = require('./remove-uploaded-files') | ||
|  | 
 | ||
|  | function drainStream (stream) { | ||
|  |   stream.on('readable', stream.read.bind(stream)) | ||
|  | } | ||
|  | 
 | ||
|  | function makeMiddleware (setup) { | ||
|  |   return function multerMiddleware (req, res, next) { | ||
|  |     if (!is(req, ['multipart'])) return next() | ||
|  | 
 | ||
|  |     var options = setup() | ||
|  | 
 | ||
|  |     var limits = options.limits | ||
|  |     var storage = options.storage | ||
|  |     var fileFilter = options.fileFilter | ||
|  |     var fileStrategy = options.fileStrategy | ||
|  |     var preservePath = options.preservePath | ||
|  | 
 | ||
|  |     req.body = Object.create(null) | ||
|  | 
 | ||
|  |     var busboy | ||
|  | 
 | ||
|  |     try { | ||
|  |       busboy = new Busboy({ headers: req.headers, limits: limits, preservePath: preservePath }) | ||
|  |     } catch (err) { | ||
|  |       return next(err) | ||
|  |     } | ||
|  | 
 | ||
|  |     var appender = new FileAppender(fileStrategy, req) | ||
|  |     var isDone = false | ||
|  |     var readFinished = false | ||
|  |     var errorOccured = false | ||
|  |     var pendingWrites = new Counter() | ||
|  |     var uploadedFiles = [] | ||
|  | 
 | ||
|  |     function done (err) { | ||
|  |       if (isDone) return | ||
|  |       isDone = true | ||
|  | 
 | ||
|  |       req.unpipe(busboy) | ||
|  |       drainStream(req) | ||
|  |       busboy.removeAllListeners() | ||
|  | 
 | ||
|  |       onFinished(req, function () { next(err) }) | ||
|  |     } | ||
|  | 
 | ||
|  |     function indicateDone () { | ||
|  |       if (readFinished && pendingWrites.isZero() && !errorOccured) done() | ||
|  |     } | ||
|  | 
 | ||
|  |     function abortWithError (uploadError) { | ||
|  |       if (errorOccured) return | ||
|  |       errorOccured = true | ||
|  | 
 | ||
|  |       pendingWrites.onceZero(function () { | ||
|  |         function remove (file, cb) { | ||
|  |           storage._removeFile(req, file, cb) | ||
|  |         } | ||
|  | 
 | ||
|  |         removeUploadedFiles(uploadedFiles, remove, function (err, storageErrors) { | ||
|  |           if (err) return done(err) | ||
|  | 
 | ||
|  |           uploadError.storageErrors = storageErrors | ||
|  |           done(uploadError) | ||
|  |         }) | ||
|  |       }) | ||
|  |     } | ||
|  | 
 | ||
|  |     function abortWithCode (code, optionalField) { | ||
|  |       abortWithError(new MulterError(code, optionalField)) | ||
|  |     } | ||
|  | 
 | ||
|  |     // handle text field data
 | ||
|  |     busboy.on('field', function (fieldname, value, fieldnameTruncated, valueTruncated) { | ||
|  |       if (fieldname == null) return abortWithCode('MISSING_FIELD_NAME') | ||
|  |       if (fieldnameTruncated) return abortWithCode('LIMIT_FIELD_KEY') | ||
|  |       if (valueTruncated) return abortWithCode('LIMIT_FIELD_VALUE', fieldname) | ||
|  | 
 | ||
|  |       // Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)
 | ||
|  |       if (limits && Object.prototype.hasOwnProperty.call(limits, 'fieldNameSize')) { | ||
|  |         if (fieldname.length > limits.fieldNameSize) return abortWithCode('LIMIT_FIELD_KEY') | ||
|  |       } | ||
|  | 
 | ||
|  |       appendField(req.body, fieldname, value) | ||
|  |     }) | ||
|  | 
 | ||
|  |     // handle files
 | ||
|  |     busboy.on('file', function (fieldname, fileStream, filename, encoding, mimetype) { | ||
|  |       // don't attach to the files object, if there is no file
 | ||
|  |       if (!filename) return fileStream.resume() | ||
|  | 
 | ||
|  |       // Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)
 | ||
|  |       if (limits && Object.prototype.hasOwnProperty.call(limits, 'fieldNameSize')) { | ||
|  |         if (fieldname.length > limits.fieldNameSize) return abortWithCode('LIMIT_FIELD_KEY') | ||
|  |       } | ||
|  | 
 | ||
|  |       var file = { | ||
|  |         fieldname: fieldname, | ||
|  |         originalname: filename, | ||
|  |         encoding: encoding, | ||
|  |         mimetype: mimetype | ||
|  |       } | ||
|  | 
 | ||
|  |       var placeholder = appender.insertPlaceholder(file) | ||
|  | 
 | ||
|  |       fileFilter(req, file, function (err, includeFile) { | ||
|  |         if (err) { | ||
|  |           appender.removePlaceholder(placeholder) | ||
|  |           return abortWithError(err) | ||
|  |         } | ||
|  | 
 | ||
|  |         if (!includeFile) { | ||
|  |           appender.removePlaceholder(placeholder) | ||
|  |           return fileStream.resume() | ||
|  |         } | ||
|  | 
 | ||
|  |         var aborting = false | ||
|  |         pendingWrites.increment() | ||
|  | 
 | ||
|  |         Object.defineProperty(file, 'stream', { | ||
|  |           configurable: true, | ||
|  |           enumerable: false, | ||
|  |           value: fileStream | ||
|  |         }) | ||
|  | 
 | ||
|  |         fileStream.on('error', function (err) { | ||
|  |           pendingWrites.decrement() | ||
|  |           abortWithError(err) | ||
|  |         }) | ||
|  | 
 | ||
|  |         fileStream.on('limit', function () { | ||
|  |           aborting = true | ||
|  |           abortWithCode('LIMIT_FILE_SIZE', fieldname) | ||
|  |         }) | ||
|  | 
 | ||
|  |         storage._handleFile(req, file, function (err, info) { | ||
|  |           if (aborting) { | ||
|  |             appender.removePlaceholder(placeholder) | ||
|  |             uploadedFiles.push(extend(file, info)) | ||
|  |             return pendingWrites.decrement() | ||
|  |           } | ||
|  | 
 | ||
|  |           if (err) { | ||
|  |             appender.removePlaceholder(placeholder) | ||
|  |             pendingWrites.decrement() | ||
|  |             return abortWithError(err) | ||
|  |           } | ||
|  | 
 | ||
|  |           var fileInfo = extend(file, info) | ||
|  | 
 | ||
|  |           appender.replacePlaceholder(placeholder, fileInfo) | ||
|  |           uploadedFiles.push(fileInfo) | ||
|  |           pendingWrites.decrement() | ||
|  |           indicateDone() | ||
|  |         }) | ||
|  |       }) | ||
|  |     }) | ||
|  | 
 | ||
|  |     busboy.on('error', function (err) { abortWithError(err) }) | ||
|  |     busboy.on('partsLimit', function () { abortWithCode('LIMIT_PART_COUNT') }) | ||
|  |     busboy.on('filesLimit', function () { abortWithCode('LIMIT_FILE_COUNT') }) | ||
|  |     busboy.on('fieldsLimit', function () { abortWithCode('LIMIT_FIELD_COUNT') }) | ||
|  |     busboy.on('finish', function () { | ||
|  |       readFinished = true | ||
|  |       indicateDone() | ||
|  |     }) | ||
|  | 
 | ||
|  |     req.pipe(busboy) | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = makeMiddleware |