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.
		
		
		
		
		
			
		
			
				
					
					
						
							174 lines
						
					
					
						
							5.0 KiB
						
					
					
				
			
		
		
	
	
							174 lines
						
					
					
						
							5.0 KiB
						
					
					
				| var is = require('type-is')
 | |
| var Busboy = require('busboy')
 | |
| var extend = require('xtend')
 | |
| 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 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 = 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)
 | |
|       busboy.removeAllListeners()
 | |
|       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, { nameTruncated, valueTruncated }) {
 | |
|       if (fieldname == null) return abortWithCode('MISSING_FIELD_NAME')
 | |
|       if (nameTruncated) 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('close', function () {
 | |
|       readFinished = true
 | |
|       indicateDone()
 | |
|     })
 | |
| 
 | |
|     req.pipe(busboy)
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = makeMiddleware
 |