Skip to content

Commit 2496943

Browse files
committedJul 6, 2015
better file selection api
1 parent fef01ed commit 2496943

13 files changed

+524
-267
lines changed
 

‎index.js

+49-102
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,83 @@
1-
var is = require('type-is')
2-
var Busboy = require('busboy')
3-
var extend = require('xtend')
4-
var appendField = require('append-field')
5-
61
var makeError = require('./lib/make-error')
7-
var selectField = require('./lib/select-field')
2+
var makeMiddleware = require('./lib/make-middleware')
3+
84
var diskStorage = require('./storage/disk')
95
var memoryStorage = require('./storage/memory')
106

117
function allowAll (req, file, cb) {
128
cb(null, true)
139
}
1410

15-
function multer (options) {
16-
options = options || {}
17-
18-
var storage
11+
function Multer (options) {
1912
if (options.storage) {
20-
storage = options.storage
13+
this.storage = options.storage
14+
} else if (options.dest) {
15+
this.storage = diskStorage({ destination: options.dest })
2116
} else {
22-
storage = diskStorage({ destination: options.dest })
17+
this.storage = memoryStorage()
2318
}
2419

25-
var fileFilter = (options.fileFilter || allowAll)
26-
27-
return function multerMiddleware (req, res, next) {
28-
if (!is(req, ['multipart'])) return next()
29-
30-
req.body = Object.create(null)
31-
req.files = []
32-
33-
var busboy = new Busboy(extend(options, { headers: req.headers }))
34-
var isDone = false
35-
var readFinished = false
36-
var pendingWrites = 0
37-
38-
function done (err) {
39-
if (isDone) return
40-
isDone = true
41-
req.unpipe(busboy)
42-
busboy.removeAllListeners()
43-
next(err)
44-
}
20+
this.limits = options.limits
21+
this.fileFilter = options.fileFilter || allowAll
22+
}
4523

46-
function indicateDone () {
47-
if (readFinished && pendingWrites === 0) done()
48-
}
24+
Multer.prototype._makeMiddleware = function (fields, fileStrategy) {
25+
function setup () {
26+
var fileFilter = this.fileFilter
27+
var filesLeft = Object.create(null)
4928

50-
function abort (errOrCode, optionalField) {
51-
if (typeof errOrCode === 'string') {
52-
done(makeError(errOrCode, optionalField))
29+
fields.forEach(function (field) {
30+
if (typeof field.maxCount === 'number') {
31+
filesLeft[field.name] = field.maxCount
5332
} else {
54-
done(errOrCode)
55-
}
56-
}
57-
58-
// handle text field data
59-
busboy.on('field', function (fieldname, value, fieldnameTruncated, valueTruncated) {
60-
if (fieldnameTruncated) return abort('LIMIT_FIELD_KEY')
61-
if (valueTruncated) return abort('LIMIT_FIELD_VALUE', fieldname)
62-
63-
// Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)
64-
if (options.limits && options.limits.hasOwnProperty('fieldNameSize')) {
65-
if (fieldname.length > options.limits.fieldNameSize) return abort('LIMIT_FIELD_KEY')
33+
filesLeft[field.name] = Infinity
6634
}
67-
68-
appendField(req.body, fieldname, value)
6935
})
7036

71-
// handle files
72-
busboy.on('file', function (fieldname, fileStream, filename, encoding, mimetype) {
73-
// don't attach to the files object, if there is no file
74-
if (!filename) return fileStream.resume()
75-
76-
// Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)
77-
if (options.limits && options.limits.hasOwnProperty('fieldNameSize')) {
78-
if (fieldname.length > options.limits.fieldNameSize) return abort('LIMIT_FIELD_KEY')
37+
function wrappedFileFilter (req, file, cb) {
38+
if ((filesLeft[file.fieldname] || 0) <= 0) {
39+
return cb(makeError('LIMIT_UNEXPECTED_FILE', file.fieldname))
7940
}
8041

81-
var file = {
82-
fieldname: fieldname,
83-
originalname: filename,
84-
encoding: encoding,
85-
mimetype: mimetype
86-
}
87-
88-
fileFilter(req, file, function (err, includeFile) {
89-
if (err) return abort(err)
90-
if (!includeFile) return fileStream.resume()
91-
92-
// defines is processing a new file
93-
pendingWrites++
94-
95-
Object.defineProperty(file, 'stream', {
96-
configurable: true,
97-
enumerable: false,
98-
value: fileStream
99-
})
100-
101-
fileStream.on('error', abort)
102-
fileStream.on('limit', function () {
103-
abort('LIMIT_FILE_SIZE', fieldname)
104-
})
42+
filesLeft[file.fieldname] -= 1
43+
fileFilter(req, file, cb)
44+
}
10545

106-
storage.handleFile(req, file, function (err, info) {
107-
if (err) return abort(err)
46+
return {
47+
limits: this.limits,
48+
storage: this.storage,
49+
fileFilter: wrappedFileFilter,
50+
fileStrategy: fileStrategy
51+
}
52+
}
10853

109-
req.files.push(extend(file, info))
54+
return makeMiddleware(setup.bind(this))
55+
}
11056

111-
pendingWrites--
112-
indicateDone()
113-
})
57+
Multer.prototype.single = function (name) {
58+
return this._makeMiddleware([{ name: name, maxCount: 1 }], 'VALUE')
59+
}
11460

115-
})
61+
Multer.prototype.array = function (name, maxCount) {
62+
return this._makeMiddleware([{ name: name, maxCount: maxCount }], 'ARRAY')
63+
}
11664

117-
})
65+
Multer.prototype.fields = function (fields) {
66+
return this._makeMiddleware(fields, 'OBJECT')
67+
}
11868

119-
busboy.on('partsLimit', function () { abort('LIMIT_PART_COUNT') })
120-
busboy.on('filesLimit', function () { abort('LIMIT_FILE_COUNT') })
121-
busboy.on('fieldsLimit', function () { abort('LIMIT_FIELD_COUNT') })
122-
busboy.on('finish', function () {
123-
readFinished = true
124-
indicateDone()
125-
})
69+
function multer (options) {
70+
if (options === undefined) {
71+
return new Multer({})
72+
}
12673

127-
req.pipe(busboy)
74+
if (typeof options === 'object' && options !== null) {
75+
return new Multer(options)
12876
}
12977

78+
throw new TypeError('Expected object for argument options')
13079
}
13180

13281
module.exports = multer
133-
module.exports.one = selectField.one
134-
module.exports.many = selectField.many
13582
module.exports.diskStorage = diskStorage
13683
module.exports.memoryStorage = memoryStorage

‎lib/make-error.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ var errorMessages = {
44
'LIMIT_FILE_COUNT': 'Too many files',
55
'LIMIT_FIELD_KEY': 'Field name too long',
66
'LIMIT_FIELD_VALUE': 'Field value too long',
7-
'LIMIT_FIELD_COUNT': 'Too many fields'
7+
'LIMIT_FIELD_COUNT': 'Too many fields',
8+
'LIMIT_UNEXPECTED_FILE': 'Unexpected field'
89
}
910

1011
function makeError (code, optionalField) {

‎lib/make-middleware.js

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
var is = require('type-is')
2+
var Busboy = require('busboy')
3+
var extend = require('xtend')
4+
var appendField = require('append-field')
5+
6+
var makeError = require('./make-error')
7+
8+
function makeMiddleware (setup) {
9+
return function multerMiddleware (req, res, next) {
10+
if (!is(req, ['multipart'])) return next()
11+
12+
var options = setup()
13+
14+
var limits = options.limits
15+
var storage = options.storage
16+
var fileFilter = options.fileFilter
17+
var fileStrategy = options.fileStrategy
18+
19+
switch (fileStrategy) {
20+
case 'VALUE': break
21+
case 'ARRAY': req.files = []; break
22+
case 'OBJECT': req.files = Object.create(null); break
23+
default: throw new Error('Unknown file strategy: ' + fileStrategy)
24+
}
25+
26+
req.body = Object.create(null)
27+
28+
var busboy = new Busboy({ headers: req.headers, limits: limits })
29+
var isDone = false
30+
var readFinished = false
31+
var pendingWrites = 0
32+
33+
function done (err) {
34+
if (isDone) return
35+
isDone = true
36+
req.unpipe(busboy)
37+
busboy.removeAllListeners()
38+
next(err)
39+
}
40+
41+
function indicateDone () {
42+
if (readFinished && pendingWrites === 0) done()
43+
}
44+
45+
function abort (errOrCode, optionalField) {
46+
if (typeof errOrCode === 'string') {
47+
done(makeError(errOrCode, optionalField))
48+
} else {
49+
done(errOrCode)
50+
}
51+
}
52+
53+
// handle text field data
54+
busboy.on('field', function (fieldname, value, fieldnameTruncated, valueTruncated) {
55+
if (fieldnameTruncated) return abort('LIMIT_FIELD_KEY')
56+
if (valueTruncated) return abort('LIMIT_FIELD_VALUE', fieldname)
57+
58+
// Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)
59+
if (options.limits && options.limits.hasOwnProperty('fieldNameSize')) {
60+
if (fieldname.length > options.limits.fieldNameSize) return abort('LIMIT_FIELD_KEY')
61+
}
62+
63+
appendField(req.body, fieldname, value)
64+
})
65+
66+
// handle files
67+
busboy.on('file', function (fieldname, fileStream, filename, encoding, mimetype) {
68+
// don't attach to the files object, if there is no file
69+
if (!filename) return fileStream.resume()
70+
71+
// Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)
72+
if (options.limits && options.limits.hasOwnProperty('fieldNameSize')) {
73+
if (fieldname.length > options.limits.fieldNameSize) return abort('LIMIT_FIELD_KEY')
74+
}
75+
76+
var file = {
77+
fieldname: fieldname,
78+
originalname: filename,
79+
encoding: encoding,
80+
mimetype: mimetype
81+
}
82+
83+
fileFilter(req, file, function (err, includeFile) {
84+
if (err) return abort(err)
85+
if (!includeFile) return fileStream.resume()
86+
87+
// defines is processing a new file
88+
pendingWrites++
89+
90+
Object.defineProperty(file, 'stream', {
91+
configurable: true,
92+
enumerable: false,
93+
value: fileStream
94+
})
95+
96+
fileStream.on('error', abort)
97+
fileStream.on('limit', function () {
98+
abort('LIMIT_FILE_SIZE', fieldname)
99+
})
100+
101+
storage.handleFile(req, file, function (err, info) {
102+
if (err) return abort(err)
103+
104+
switch (fileStrategy) {
105+
case 'VALUE': req.file = extend(file, info); break
106+
case 'ARRAY': req.files.push(extend(file, info)); break
107+
case 'OBJECT':
108+
if (req.files[fieldname]) {
109+
req.files[fieldname].push(extend(file, info))
110+
} else {
111+
req.files[fieldname] = [extend(file, info)]
112+
}
113+
break
114+
}
115+
116+
pendingWrites--
117+
indicateDone()
118+
})
119+
120+
})
121+
122+
})
123+
124+
busboy.on('partsLimit', function () { abort('LIMIT_PART_COUNT') })
125+
busboy.on('filesLimit', function () { abort('LIMIT_FILE_COUNT') })
126+
busboy.on('fieldsLimit', function () { abort('LIMIT_FIELD_COUNT') })
127+
busboy.on('finish', function () {
128+
readFinished = true
129+
indicateDone()
130+
})
131+
132+
req.pipe(busboy)
133+
}
134+
}
135+
136+
module.exports = makeMiddleware

‎lib/select-field.js

-22
This file was deleted.

‎test/disk-storage.js

+55-48
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ var rimraf = require('rimraf')
99
var FormData = require('form-data')
1010

1111
describe('Disk Storage', function () {
12-
var uploadDir, parser
12+
var uploadDir, upload
1313

1414
before(function (done) {
1515
temp.mkdir(function (err, path) {
1616
if (err) return done(err)
1717

1818
uploadDir = path
19-
parser = multer({ dest: path })
19+
upload = multer({ dest: path })
2020
done()
2121
})
2222
})
@@ -27,6 +27,7 @@ describe('Disk Storage', function () {
2727

2828
it('should process parser/form-data POST request', function (done) {
2929
var form = new FormData()
30+
var parser = upload.single('small0')
3031

3132
form.append('name', 'Multer')
3233
form.append('small0', util.file('small0.dat'))
@@ -36,11 +37,10 @@ describe('Disk Storage', function () {
3637

3738
assert.equal(req.body.name, 'Multer')
3839

39-
assert.equal(req.files.length, 1)
40-
assert.equal(req.files[0].fieldname, 'small0')
41-
assert.equal(req.files[0].originalname, 'small0.dat')
42-
assert.equal(req.files[0].size, 1778)
43-
assert.equal(util.fileSize(req.files[0].path), 1778)
40+
assert.equal(req.file.fieldname, 'small0')
41+
assert.equal(req.file.originalname, 'small0.dat')
42+
assert.equal(req.file.size, 1778)
43+
assert.equal(util.fileSize(req.file.path), 1778)
4444

4545
done()
4646
})
@@ -49,6 +49,7 @@ describe('Disk Storage', function () {
4949

5050
it('should process empty fields and an empty file', function (done) {
5151
var form = new FormData()
52+
var parser = upload.single('empty')
5253

5354
form.append('empty', util.file('empty.dat'))
5455
form.append('name', 'Multer')
@@ -72,11 +73,10 @@ describe('Disk Storage', function () {
7273
assert.deepEqual(req.body.checkboxhalfempty, [ 'cb1', '' ])
7374
assert.deepEqual(req.body.checkboxempty, [ '', '' ])
7475

75-
assert.equal(req.files.length, 1)
76-
assert.equal(req.files[0].fieldname, 'empty')
77-
assert.equal(req.files[0].originalname, 'empty.dat')
78-
assert.equal(req.files[0].size, 0)
79-
assert.equal(util.fileSize(req.files[0].path), 0)
76+
assert.equal(req.file.fieldname, 'empty')
77+
assert.equal(req.file.originalname, 'empty.dat')
78+
assert.equal(req.file.size, 0)
79+
assert.equal(util.fileSize(req.file.path), 0)
8080

8181
done()
8282
})
@@ -85,6 +85,15 @@ describe('Disk Storage', function () {
8585

8686
it('should process multiple files', function (done) {
8787
var form = new FormData()
88+
var parser = upload.fields([
89+
{ name: 'empty', maxCount: 1 },
90+
{ name: 'tiny0', maxCount: 1 },
91+
{ name: 'tiny1', maxCount: 1 },
92+
{ name: 'small0', maxCount: 1 },
93+
{ name: 'small1', maxCount: 1 },
94+
{ name: 'medium', maxCount: 1 },
95+
{ name: 'large', maxCount: 1 }
96+
])
8897

8998
form.append('empty', util.file('empty.dat'))
9099
form.append('tiny0', util.file('tiny0.dat'))
@@ -99,42 +108,40 @@ describe('Disk Storage', function () {
99108

100109
assert.deepEqual(req.body, {})
101110

102-
assert.equal(req.files.length, 7)
103-
104-
assert.equal(req.files[0].fieldname, 'empty')
105-
assert.equal(req.files[0].originalname, 'empty.dat')
106-
assert.equal(req.files[0].size, 0)
107-
assert.equal(util.fileSize(req.files[0].path), 0)
108-
109-
assert.equal(req.files[1].fieldname, 'tiny0')
110-
assert.equal(req.files[1].originalname, 'tiny0.dat')
111-
assert.equal(req.files[1].size, 122)
112-
assert.equal(util.fileSize(req.files[1].path), 122)
113-
114-
assert.equal(req.files[2].fieldname, 'tiny1')
115-
assert.equal(req.files[2].originalname, 'tiny1.dat')
116-
assert.equal(req.files[2].size, 7)
117-
assert.equal(util.fileSize(req.files[2].path), 7)
118-
119-
assert.equal(req.files[3].fieldname, 'small0')
120-
assert.equal(req.files[3].originalname, 'small0.dat')
121-
assert.equal(req.files[3].size, 1778)
122-
assert.equal(util.fileSize(req.files[3].path), 1778)
123-
124-
assert.equal(req.files[4].fieldname, 'small1')
125-
assert.equal(req.files[4].originalname, 'small1.dat')
126-
assert.equal(req.files[4].size, 315)
127-
assert.equal(util.fileSize(req.files[4].path), 315)
128-
129-
assert.equal(req.files[5].fieldname, 'medium')
130-
assert.equal(req.files[5].originalname, 'medium.dat')
131-
assert.equal(req.files[5].size, 13196)
132-
assert.equal(util.fileSize(req.files[5].path), 13196)
133-
134-
assert.equal(req.files[6].fieldname, 'large')
135-
assert.equal(req.files[6].originalname, 'large.jpg')
136-
assert.equal(req.files[6].size, 2413677)
137-
assert.equal(util.fileSize(req.files[6].path), 2413677)
111+
assert.equal(req.files['empty'][0].fieldname, 'empty')
112+
assert.equal(req.files['empty'][0].originalname, 'empty.dat')
113+
assert.equal(req.files['empty'][0].size, 0)
114+
assert.equal(util.fileSize(req.files['empty'][0].path), 0)
115+
116+
assert.equal(req.files['tiny0'][0].fieldname, 'tiny0')
117+
assert.equal(req.files['tiny0'][0].originalname, 'tiny0.dat')
118+
assert.equal(req.files['tiny0'][0].size, 122)
119+
assert.equal(util.fileSize(req.files['tiny0'][0].path), 122)
120+
121+
assert.equal(req.files['tiny1'][0].fieldname, 'tiny1')
122+
assert.equal(req.files['tiny1'][0].originalname, 'tiny1.dat')
123+
assert.equal(req.files['tiny1'][0].size, 7)
124+
assert.equal(util.fileSize(req.files['tiny1'][0].path), 7)
125+
126+
assert.equal(req.files['small0'][0].fieldname, 'small0')
127+
assert.equal(req.files['small0'][0].originalname, 'small0.dat')
128+
assert.equal(req.files['small0'][0].size, 1778)
129+
assert.equal(util.fileSize(req.files['small0'][0].path), 1778)
130+
131+
assert.equal(req.files['small1'][0].fieldname, 'small1')
132+
assert.equal(req.files['small1'][0].originalname, 'small1.dat')
133+
assert.equal(req.files['small1'][0].size, 315)
134+
assert.equal(util.fileSize(req.files['small1'][0].path), 315)
135+
136+
assert.equal(req.files['medium'][0].fieldname, 'medium')
137+
assert.equal(req.files['medium'][0].originalname, 'medium.dat')
138+
assert.equal(req.files['medium'][0].size, 13196)
139+
assert.equal(util.fileSize(req.files['medium'][0].path), 13196)
140+
141+
assert.equal(req.files['large'][0].fieldname, 'large')
142+
assert.equal(req.files['large'][0].originalname, 'large.jpg')
143+
assert.equal(req.files['large'][0].size, 2413677)
144+
assert.equal(util.fileSize(req.files['large'][0].path), 2413677)
138145

139146
done()
140147
})

‎test/error-handling.js

+34-9
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ var util = require('./_util')
66
var multer = require('../')
77
var FormData = require('form-data')
88

9-
function withLimits (limits) {
9+
function withLimits (limits, fields) {
1010
var storage = multer.memoryStorage()
11-
return multer({ storage: storage, limits: limits })
11+
return multer({ storage: storage, limits: limits }).fields(fields)
1212
}
1313

1414
describe('Error Handling', function () {
1515
it('should respect parts limit', function (done) {
1616
var form = new FormData()
17-
var parser = withLimits({ parts: 1 })
17+
var parser = withLimits({ parts: 1 }, [
18+
{ name: 'small0', maxCount: 1 }
19+
])
1820

1921
form.append('field0', 'BOOM!')
2022
form.append('small0', util.file('small0.dat'))
@@ -27,7 +29,10 @@ describe('Error Handling', function () {
2729

2830
it('should respect file size limit', function (done) {
2931
var form = new FormData()
30-
var parser = withLimits({ fileSize: 1500 })
32+
var parser = withLimits({ fileSize: 1500 }, [
33+
{ name: 'tiny0', maxCount: 1 },
34+
{ name: 'small0', maxCount: 1 }
35+
])
3136

3237
form.append('tiny0', util.file('tiny0.dat'))
3338
form.append('small0', util.file('small0.dat'))
@@ -41,7 +46,10 @@ describe('Error Handling', function () {
4146

4247
it('should respect file count limit', function (done) {
4348
var form = new FormData()
44-
var parser = withLimits({ files: 1 })
49+
var parser = withLimits({ files: 1 }, [
50+
{ name: 'small0', maxCount: 1 },
51+
{ name: 'small1', maxCount: 1 }
52+
])
4553

4654
form.append('small0', util.file('small0.dat'))
4755
form.append('small1', util.file('small1.dat'))
@@ -54,7 +62,9 @@ describe('Error Handling', function () {
5462

5563
it('should respect file key limit', function (done) {
5664
var form = new FormData()
57-
var parser = withLimits({ fieldNameSize: 4 })
65+
var parser = withLimits({ fieldNameSize: 4 }, [
66+
{ name: 'small0', maxCount: 1 }
67+
])
5868

5969
form.append('small0', util.file('small0.dat'))
6070

@@ -66,7 +76,7 @@ describe('Error Handling', function () {
6676

6777
it('should respect field key limit', function (done) {
6878
var form = new FormData()
69-
var parser = withLimits({ fieldNameSize: 4 })
79+
var parser = withLimits({ fieldNameSize: 4 }, [])
7080

7181
form.append('ok', 'SMILE')
7282
form.append('blowup', 'BOOM!')
@@ -79,7 +89,7 @@ describe('Error Handling', function () {
7989

8090
it('should respect field value limit', function (done) {
8191
var form = new FormData()
82-
var parser = withLimits({ fieldSize: 16 })
92+
var parser = withLimits({ fieldSize: 16 }, [])
8393

8494
form.append('field0', 'This is okay')
8595
form.append('field1', 'This will make the parser explode')
@@ -93,7 +103,7 @@ describe('Error Handling', function () {
93103

94104
it('should respect field count limit', function (done) {
95105
var form = new FormData()
96-
var parser = withLimits({ fields: 1 })
106+
var parser = withLimits({ fields: 1 }, [])
97107

98108
form.append('field0', 'BOOM!')
99109
form.append('field1', 'BOOM!')
@@ -103,4 +113,19 @@ describe('Error Handling', function () {
103113
done()
104114
})
105115
})
116+
117+
it('should respect fields given', function (done) {
118+
var form = new FormData()
119+
var parser = withLimits(undefined, [
120+
{ name: 'wrongname', maxCount: 1 }
121+
])
122+
123+
form.append('small0', util.file('small0.dat'))
124+
125+
util.submitForm(parser, form, function (err, req) {
126+
assert.equal(err.code, 'LIMIT_UNEXPECTED_FILE')
127+
assert.equal(err.field, 'small0')
128+
done()
129+
})
130+
})
106131
})

‎test/expected-files.js

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* eslint-env mocha */
2+
3+
var assert = require('assert')
4+
5+
var util = require('./_util')
6+
var multer = require('../')
7+
var FormData = require('form-data')
8+
9+
describe('Expected files', function () {
10+
var upload
11+
12+
before(function (done) {
13+
upload = multer()
14+
done()
15+
})
16+
17+
it('should reject single unexpected file', function (done) {
18+
var form = new FormData()
19+
var parser = upload.single('butme')
20+
21+
form.append('notme', util.file('small0.dat'))
22+
23+
util.submitForm(parser, form, function (err, req) {
24+
assert.equal(err.code, 'LIMIT_UNEXPECTED_FILE')
25+
assert.equal(err.field, 'notme')
26+
done()
27+
})
28+
})
29+
30+
it('should reject array of multiple files', function (done) {
31+
var form = new FormData()
32+
var parser = upload.array('butme', 4)
33+
34+
form.append('notme', util.file('small0.dat'))
35+
form.append('notme', util.file('small1.dat'))
36+
37+
util.submitForm(parser, form, function (err, req) {
38+
assert.equal(err.code, 'LIMIT_UNEXPECTED_FILE')
39+
assert.equal(err.field, 'notme')
40+
done()
41+
})
42+
})
43+
44+
it('should reject overflowing arrays', function (done) {
45+
var form = new FormData()
46+
var parser = upload.array('butme', 1)
47+
48+
form.append('butme', util.file('small0.dat'))
49+
form.append('butme', util.file('small1.dat'))
50+
51+
util.submitForm(parser, form, function (err, req) {
52+
assert.equal(err.code, 'LIMIT_UNEXPECTED_FILE')
53+
assert.equal(err.field, 'butme')
54+
done()
55+
})
56+
})
57+
58+
it('should accept files with expected fieldname', function (done) {
59+
var form = new FormData()
60+
var parser = upload.fields([
61+
{ name: 'butme', maxCount: 2 },
62+
{ name: 'andme', maxCount: 2 }
63+
])
64+
65+
form.append('butme', util.file('small0.dat'))
66+
form.append('butme', util.file('small1.dat'))
67+
form.append('andme', util.file('empty.dat'))
68+
69+
util.submitForm(parser, form, function (err, req) {
70+
assert.ifError(err)
71+
72+
assert.equal(req.files['butme'].length, 2)
73+
assert.equal(req.files['andme'].length, 1)
74+
75+
done()
76+
})
77+
})
78+
79+
it('should reject files with unexpected fieldname', function (done) {
80+
var form = new FormData()
81+
var parser = upload.fields([
82+
{ name: 'butme', maxCount: 2 },
83+
{ name: 'andme', maxCount: 2 }
84+
])
85+
86+
form.append('butme', util.file('small0.dat'))
87+
form.append('butme', util.file('small1.dat'))
88+
form.append('andme', util.file('empty.dat'))
89+
form.append('notme', util.file('empty.dat'))
90+
91+
util.submitForm(parser, form, function (err, req) {
92+
assert.equal(err.code, 'LIMIT_UNEXPECTED_FILE')
93+
assert.equal(err.field, 'notme')
94+
done()
95+
})
96+
})
97+
})

‎test/fields.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('Fields', function () {
1212
var parser
1313

1414
before(function () {
15-
parser = multer({ storage: multer.memoryStorage() })
15+
parser = multer().fields([])
1616
})
1717

1818
it('should process multiple fields', function (done) {

‎test/file-filter.js

+11-8
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ var multer = require('../')
77
var FormData = require('form-data')
88

99
describe('File Filter', function () {
10-
var parser
10+
var upload
1111

1212
before(function () {
13-
parser = multer({
14-
storage: multer.memoryStorage(),
13+
upload = multer({
1514
fileFilter: function (req, file, cb) {
1615
cb(null, file.fieldname !== 'notme')
1716
}
@@ -20,17 +19,21 @@ describe('File Filter', function () {
2019

2120
it('should skip some files', function (done) {
2221
var form = new FormData()
22+
var parser = upload.fields([
23+
{ name: 'notme', maxCount: 1 },
24+
{ name: 'butme', maxCount: 1 }
25+
])
2326

2427
form.append('notme', util.file('tiny0.dat'))
2528
form.append('butme', util.file('tiny1.dat'))
2629

2730
util.submitForm(parser, form, function (err, req) {
2831
assert.ifError(err)
29-
assert.equal(req.files.length, 1)
30-
assert.equal(req.files[0].fieldname, 'butme')
31-
assert.equal(req.files[0].originalname, 'tiny1.dat')
32-
assert.equal(req.files[0].size, 7)
33-
assert.equal(req.files[0].buffer.length, 7)
32+
assert.equal(req.files['notme'], undefined)
33+
assert.equal(req.files['butme'][0].fieldname, 'butme')
34+
assert.equal(req.files['butme'][0].originalname, 'tiny1.dat')
35+
assert.equal(req.files['butme'][0].size, 7)
36+
assert.equal(req.files['butme'][0].buffer.length, 7)
3437
done()
3538
})
3639
})

‎test/functionality.js

+15-11
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('Functionality', function () {
3131
})
3232

3333
cb(null, {
34-
parser: multer({ storage: storage }),
34+
upload: multer({ storage: storage }),
3535
uploadDir: uploadDir,
3636
form: new FormData()
3737
})
@@ -46,12 +46,13 @@ describe('Functionality', function () {
4646
makeStandardEnv(function (err, env) {
4747
if (err) return done(err)
4848

49+
var parser = env.upload.single('small0')
4950
env.form.append('small0', util.file('small0.dat'))
5051

51-
util.submitForm(env.parser, env.form, function (err, req) {
52+
util.submitForm(parser, env.form, function (err, req) {
5253
assert.ifError(err)
53-
assert.ok(startsWith(req.files[0].path, env.uploadDir))
54-
assert.equal(util.fileSize(req.files[0].path), 1778)
54+
assert.ok(startsWith(req.file.path, env.uploadDir))
55+
assert.equal(util.fileSize(req.file.path), 1778)
5556
done()
5657
})
5758
})
@@ -61,11 +62,12 @@ describe('Functionality', function () {
6162
makeStandardEnv(function (err, env) {
6263
if (err) return done(err)
6364

65+
var parser = env.upload.single('small0')
6466
env.form.append('small0', util.file('small0.dat'))
6567

66-
util.submitForm(env.parser, env.form, function (err, req) {
68+
util.submitForm(parser, env.form, function (err, req) {
6769
assert.ifError(err)
68-
assert.equal(req.files[0].filename, 'small0small0.dat')
70+
assert.equal(req.file.filename, 'small0small0.dat')
6971
done()
7072
})
7173
})
@@ -75,12 +77,12 @@ describe('Functionality', function () {
7577
makeStandardEnv(function (err, env) {
7678
if (err) return done(err)
7779

80+
var parser = env.upload.single('tiny0')
7881
env.form.append('tiny0', util.file('tiny0.dat'))
7982

80-
util.submitForm(env.parser, env.form, function (err, req) {
83+
util.submitForm(parser, env.form, function (err, req) {
8184
assert.ifError(err)
82-
assert.equal(req.files.length, 1)
83-
assert.equal(req.files[0].filename, 'tiny0tiny0.dat')
85+
assert.equal(req.file.filename, 'tiny0tiny0.dat')
8486
done()
8587
})
8688
})
@@ -90,10 +92,11 @@ describe('Functionality', function () {
9092
makeStandardEnv(function (err, env) {
9193
if (err) return done(err)
9294

95+
var parser = env.upload.array('themFiles', 2)
9396
env.form.append('themFiles', util.file('small0.dat'))
9497
env.form.append('themFiles', util.file('small1.dat'))
9598

96-
util.submitForm(env.parser, env.form, function (err, req) {
99+
util.submitForm(parser, env.form, function (err, req) {
97100
assert.ifError(err)
98101
assert.equal(req.files.length, 2)
99102
assert.equal(req.files[0].filename, 'themFilessmall0.dat')
@@ -116,8 +119,9 @@ describe('Functionality', function () {
116119
filename: generateFilename
117120
})
118121

119-
var parser = multer({ storage: storage })
120122
var form = new FormData()
123+
var upload = multer({ storage: storage })
124+
var parser = upload.array('themFiles', 2)
121125

122126
form.append('themFiles', util.file('small0.dat'))
123127
form.append('themFiles', util.file('small1.dat'))

‎test/memory-storage.js

+55-48
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ var multer = require('../')
77
var FormData = require('form-data')
88

99
describe('Memory Storage', function () {
10-
var parser
10+
var upload
1111

1212
before(function (done) {
13-
parser = multer({ storage: multer.memoryStorage() })
13+
upload = multer()
1414
done()
1515
})
1616

1717
it('should process multipart/form-data POST request', function (done) {
1818
var form = new FormData()
19+
var parser = upload.single('small0')
1920

2021
form.append('name', 'Multer')
2122
form.append('small0', util.file('small0.dat'))
@@ -25,11 +26,10 @@ describe('Memory Storage', function () {
2526

2627
assert.equal(req.body.name, 'Multer')
2728

28-
assert.equal(req.files.length, 1)
29-
assert.equal(req.files[0].fieldname, 'small0')
30-
assert.equal(req.files[0].originalname, 'small0.dat')
31-
assert.equal(req.files[0].size, 1778)
32-
assert.equal(req.files[0].buffer.length, 1778)
29+
assert.equal(req.file.fieldname, 'small0')
30+
assert.equal(req.file.originalname, 'small0.dat')
31+
assert.equal(req.file.size, 1778)
32+
assert.equal(req.file.buffer.length, 1778)
3333

3434
done()
3535
})
@@ -38,6 +38,7 @@ describe('Memory Storage', function () {
3838

3939
it('should process empty fields and an empty file', function (done) {
4040
var form = new FormData()
41+
var parser = upload.single('empty')
4142

4243
form.append('empty', util.file('empty.dat'))
4344
form.append('name', 'Multer')
@@ -61,11 +62,10 @@ describe('Memory Storage', function () {
6162
assert.deepEqual(req.body.checkboxhalfempty, [ 'cb1', '' ])
6263
assert.deepEqual(req.body.checkboxempty, [ '', '' ])
6364

64-
assert.equal(req.files.length, 1)
65-
assert.equal(req.files[0].fieldname, 'empty')
66-
assert.equal(req.files[0].originalname, 'empty.dat')
67-
assert.equal(req.files[0].size, 0)
68-
assert.equal(req.files[0].buffer.length, 0)
65+
assert.equal(req.file.fieldname, 'empty')
66+
assert.equal(req.file.originalname, 'empty.dat')
67+
assert.equal(req.file.size, 0)
68+
assert.equal(req.file.buffer.length, 0)
6969

7070
done()
7171
})
@@ -74,6 +74,15 @@ describe('Memory Storage', function () {
7474

7575
it('should process multiple files', function (done) {
7676
var form = new FormData()
77+
var parser = upload.fields([
78+
{ name: 'empty', maxCount: 1 },
79+
{ name: 'tiny0', maxCount: 1 },
80+
{ name: 'tiny1', maxCount: 1 },
81+
{ name: 'small0', maxCount: 1 },
82+
{ name: 'small1', maxCount: 1 },
83+
{ name: 'medium', maxCount: 1 },
84+
{ name: 'large', maxCount: 1 }
85+
])
7786

7887
form.append('empty', util.file('empty.dat'))
7988
form.append('tiny0', util.file('tiny0.dat'))
@@ -88,42 +97,40 @@ describe('Memory Storage', function () {
8897

8998
assert.deepEqual(req.body, {})
9099

91-
assert.equal(req.files.length, 7)
92-
93-
assert.equal(req.files[0].fieldname, 'empty')
94-
assert.equal(req.files[0].originalname, 'empty.dat')
95-
assert.equal(req.files[0].size, 0)
96-
assert.equal(req.files[0].buffer.length, 0)
97-
98-
assert.equal(req.files[1].fieldname, 'tiny0')
99-
assert.equal(req.files[1].originalname, 'tiny0.dat')
100-
assert.equal(req.files[1].size, 122)
101-
assert.equal(req.files[1].buffer.length, 122)
102-
103-
assert.equal(req.files[2].fieldname, 'tiny1')
104-
assert.equal(req.files[2].originalname, 'tiny1.dat')
105-
assert.equal(req.files[2].size, 7)
106-
assert.equal(req.files[2].buffer.length, 7)
107-
108-
assert.equal(req.files[3].fieldname, 'small0')
109-
assert.equal(req.files[3].originalname, 'small0.dat')
110-
assert.equal(req.files[3].size, 1778)
111-
assert.equal(req.files[3].buffer.length, 1778)
112-
113-
assert.equal(req.files[4].fieldname, 'small1')
114-
assert.equal(req.files[4].originalname, 'small1.dat')
115-
assert.equal(req.files[4].size, 315)
116-
assert.equal(req.files[4].buffer.length, 315)
117-
118-
assert.equal(req.files[5].fieldname, 'medium')
119-
assert.equal(req.files[5].originalname, 'medium.dat')
120-
assert.equal(req.files[5].size, 13196)
121-
assert.equal(req.files[5].buffer.length, 13196)
122-
123-
assert.equal(req.files[6].fieldname, 'large')
124-
assert.equal(req.files[6].originalname, 'large.jpg')
125-
assert.equal(req.files[6].size, 2413677)
126-
assert.equal(req.files[6].buffer.length, 2413677)
100+
assert.equal(req.files['empty'][0].fieldname, 'empty')
101+
assert.equal(req.files['empty'][0].originalname, 'empty.dat')
102+
assert.equal(req.files['empty'][0].size, 0)
103+
assert.equal(req.files['empty'][0].buffer.length, 0)
104+
105+
assert.equal(req.files['tiny0'][0].fieldname, 'tiny0')
106+
assert.equal(req.files['tiny0'][0].originalname, 'tiny0.dat')
107+
assert.equal(req.files['tiny0'][0].size, 122)
108+
assert.equal(req.files['tiny0'][0].buffer.length, 122)
109+
110+
assert.equal(req.files['tiny1'][0].fieldname, 'tiny1')
111+
assert.equal(req.files['tiny1'][0].originalname, 'tiny1.dat')
112+
assert.equal(req.files['tiny1'][0].size, 7)
113+
assert.equal(req.files['tiny1'][0].buffer.length, 7)
114+
115+
assert.equal(req.files['small0'][0].fieldname, 'small0')
116+
assert.equal(req.files['small0'][0].originalname, 'small0.dat')
117+
assert.equal(req.files['small0'][0].size, 1778)
118+
assert.equal(req.files['small0'][0].buffer.length, 1778)
119+
120+
assert.equal(req.files['small1'][0].fieldname, 'small1')
121+
assert.equal(req.files['small1'][0].originalname, 'small1.dat')
122+
assert.equal(req.files['small1'][0].size, 315)
123+
assert.equal(req.files['small1'][0].buffer.length, 315)
124+
125+
assert.equal(req.files['medium'][0].fieldname, 'medium')
126+
assert.equal(req.files['medium'][0].originalname, 'medium.dat')
127+
assert.equal(req.files['medium'][0].size, 13196)
128+
assert.equal(req.files['medium'][0].buffer.length, 13196)
129+
130+
assert.equal(req.files['large'][0].fieldname, 'large')
131+
assert.equal(req.files['large'][0].originalname, 'large.jpg')
132+
assert.equal(req.files['large'][0].size, 2413677)
133+
assert.equal(req.files['large'][0].buffer.length, 2413677)
127134

128135
done()
129136
})

‎test/reuse-middleware.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* eslint-env mocha */
2+
3+
var assert = require('assert')
4+
5+
var util = require('./_util')
6+
var multer = require('../')
7+
var FormData = require('form-data')
8+
9+
describe('Reuse Middleware', function () {
10+
var parser
11+
12+
before(function (done) {
13+
parser = multer().array('them-files')
14+
done()
15+
})
16+
17+
it('should accept multiple requests', function (done) {
18+
var pending = 8
19+
20+
function submitData (fileCount) {
21+
var form = new FormData()
22+
23+
form.append('name', 'Multer')
24+
form.append('files', '' + fileCount)
25+
26+
for (var i = 0; i < fileCount; i++) {
27+
form.append('them-files', util.file('small0.dat'))
28+
}
29+
30+
util.submitForm(parser, form, function (err, req) {
31+
assert.ifError(err)
32+
33+
assert.equal(req.body.name, 'Multer')
34+
assert.equal(req.body.files, '' + fileCount)
35+
assert.equal(req.files.length, fileCount)
36+
37+
req.files.forEach(function (file) {
38+
assert.equal(file.fieldname, 'them-files')
39+
assert.equal(file.originalname, 'small0.dat')
40+
assert.equal(file.size, 1778)
41+
assert.equal(file.buffer.length, 1778)
42+
})
43+
44+
if (--pending === 0) done()
45+
})
46+
}
47+
48+
submitData(9)
49+
submitData(1)
50+
submitData(5)
51+
submitData(7)
52+
submitData(2)
53+
submitData(8)
54+
submitData(3)
55+
submitData(4)
56+
})
57+
})

‎test/select-field.js

+12-17
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,32 @@ function assertSet (files, setName, fileNames) {
3131
}
3232
}
3333

34-
describe('Memory Storage', function () {
34+
describe('Select Field', function () {
3535
var parser
3636

3737
before(function () {
38-
parser = multer({ storage: multer.memoryStorage() })
38+
parser = multer().fields([
39+
{ name: 'CA$|-|', maxCount: 1 },
40+
{ name: 'set-1', maxCount: 3 },
41+
{ name: 'set-2', maxCount: 3 }
42+
])
3943
})
4044

4145
it('should select the first file with fieldname', function (done) {
4246
util.submitForm(parser, generateForm(), function (err, req) {
4347
assert.ifError(err)
44-
assert.equal(req.files.length, 7)
4548

4649
var file
4750

48-
file = multer.one(req.files, 'CA$|-|')
51+
file = req.files['CA$|-|'][0]
4952
assert.equal(file.fieldname, 'CA$|-|')
5053
assert.equal(file.originalname, 'empty.dat')
5154

52-
file = multer.one(req.files, 'set-1')
55+
file = req.files['set-1'][0]
5356
assert.equal(file.fieldname, 'set-1')
5457
assert.equal(file.originalname, 'tiny0.dat')
5558

56-
file = multer.one(req.files, 'set-2')
59+
file = req.files['set-2'][0]
5760
assert.equal(file.fieldname, 'set-2')
5861
assert.equal(file.originalname, 'tiny1.dat')
5962

@@ -65,18 +68,10 @@ describe('Memory Storage', function () {
6568
it('should select all files with fieldname', function (done) {
6669
util.submitForm(parser, generateForm(), function (err, req) {
6770
assert.ifError(err)
68-
assert.equal(req.files.length, 7)
6971

70-
var files
71-
72-
files = multer.many(req.files, 'CA$|-|')
73-
assertSet(files, 'CA$|-|', [ 'empty.dat' ])
74-
75-
files = multer.many(req.files, 'set-1')
76-
assertSet(files, 'set-1', [ 'tiny0.dat', 'empty.dat', 'tiny1.dat' ])
77-
78-
files = multer.many(req.files, 'set-2')
79-
assertSet(files, 'set-2', [ 'tiny1.dat', 'tiny0.dat', 'empty.dat' ])
72+
assertSet(req.files['CA$|-|'], 'CA$|-|', [ 'empty.dat' ])
73+
assertSet(req.files['set-1'], 'set-1', [ 'tiny0.dat', 'empty.dat', 'tiny1.dat' ])
74+
assertSet(req.files['set-2'], 'set-2', [ 'tiny1.dat', 'tiny0.dat', 'empty.dat' ])
8075

8176
done()
8277
})

0 commit comments

Comments
 (0)
Please sign in to comment.