1
- var busboy = require ( 'connect- busboy' ) ;
1
+ var Busboy = require ( 'busboy' ) ;
2
2
var fs = require ( 'fs-extra' ) ;
3
3
var streamifier = require ( 'streamifier' ) ;
4
4
5
+ var ACCEPTABLE_MIME = / ^ (?: m u l t i p a r t \/ .+ ) $ / i;
6
+ var UNACCEPTABLE_METHODS = [
7
+ 'GET' ,
8
+ 'HEAD'
9
+ ] ;
10
+
5
11
module . exports = function ( options ) {
6
12
options = options || { } ;
7
13
8
14
return function ( req , res , next ) {
9
- return busboy ( options ) ( req , res , function ( ) {
10
-
11
- // If no busboy req obj, then no uploads are taking place
12
- if ( ! req . busboy )
15
+ if ( ! hasBody ( req ) || ! hasAcceptableMethod ( req ) || ! hasAcceptableMime ( req ) )
13
16
return next ( ) ;
14
17
15
- req . files = null ;
16
-
17
- req . busboy . on ( 'field' , function ( fieldname , val , fieldnameTruncated , valTruncated , encoding , mimetype ) {
18
- req . body = req . body || { } ;
19
-
20
- var prev = req . body [ fieldname ] ;
21
-
22
- if ( ! prev )
23
- return req . body [ fieldname ] = val ;
24
-
25
- if ( Array . isArray ( prev ) )
26
- return prev . push ( val ) ;
27
-
28
- req . body [ fieldname ] = [ prev , val ] ;
29
- } ) ;
30
-
31
- req . busboy . on ( 'file' , function ( fieldname , file , filename , encoding , mimetype ) {
32
- var buf = new Buffer ( 0 ) ;
33
- var safeFileNameRegex = / [ ^ \w - ] / g;
34
-
35
- file . on ( 'data' , function ( data ) {
36
- buf = Buffer . concat ( [ buf , data ] ) ;
37
-
38
- if ( options . debug )
39
- return console . log ( 'Uploading %s -> %s' , fieldname , filename ) ;
40
- } ) ;
41
-
42
- file . on ( 'end' , function ( ) {
43
- if ( ! req . files )
44
- req . files = { } ;
45
-
46
- // see: https://github.com/richardgirges/express-fileupload/issues/14
47
- // firefox uploads empty file in case of cache miss when f5ing page.
48
- // resulting in unexpected behavior. if there is no file data, the file is invalid.
49
- if ( ! buf . length )
50
- return ;
51
-
52
- if ( options . safeFileNames ) {
53
- if ( typeof options . safeFileNames === 'object' )
54
- safeFileNameRegex = options . safeFileNames ;
55
-
56
- filename = filename . replace ( safeFileNameRegex , '' ) ;
57
- }
58
-
59
- var newFile = {
60
- name : filename ,
61
- data : buf ,
62
- encoding : encoding ,
63
- mimetype : mimetype ,
64
- mv : function ( path , callback ) {
65
- var fstream ;
66
- fstream = fs . createWriteStream ( path ) ;
67
- streamifier . createReadStream ( buf ) . pipe ( fstream ) ;
68
- fstream . on ( 'error' , function ( error ) {
69
- if ( callback )
70
- callback ( error ) ;
71
- } ) ;
72
- fstream . on ( 'close' , function ( ) {
73
- if ( callback )
74
- callback ( null ) ;
75
- } ) ;
76
- }
77
- } ;
78
-
79
- if ( ! req . files . hasOwnProperty ( fieldname ) ) {
80
- req . files [ fieldname ] = newFile ;
81
- } else {
82
- if ( req . files [ fieldname ] instanceof Array )
83
- req . files [ fieldname ] . push ( newFile ) ;
84
- else
85
- req . files [ fieldname ] = [ req . files [ fieldname ] , newFile ] ;
86
- }
87
- } ) ;
88
- } ) ;
89
-
90
- req . busboy . on ( 'finish' , next ) ;
91
-
92
- req . pipe ( req . busboy ) ;
93
- } ) ;
18
+ processMultipart ( options , req , res , next ) ;
94
19
} ;
95
20
} ;
21
+
22
+
23
+ /**
24
+ * Processes multipart request
25
+ * Builds a req.body object for fields
26
+ * Builds a req.files object for files
27
+ * @param {Object } options expressFileupload and Busboy options
28
+ * @param {Object } req Express request object
29
+ * @param {Object } res Express response object
30
+ * @param {Function } next Express next method
31
+ * @return {void }
32
+ */
33
+ function processMultipart ( options , req , res , next ) {
34
+ var busboyOptions = { } ;
35
+ var busboy ;
36
+
37
+ req . files = null ;
38
+
39
+ // Build busboy config
40
+ for ( var k in options ) {
41
+ busboyOptions [ k ] = options [ k ] ;
42
+ }
43
+
44
+ // Attach request headers to busboy config
45
+ busboyOptions . headers = req . headers ;
46
+
47
+ // Init busboy instance
48
+ busboy = new Busboy ( busboyOptions ) ;
49
+
50
+ // Build multipart req.body fields
51
+ busboy . on ( 'field' , function ( fieldname , val , fieldnameTruncated , valTruncated , encoding , mime ) {
52
+ req . body = req . body || { } ;
53
+
54
+ var prev = req . body [ fieldname ] ;
55
+
56
+ if ( ! prev )
57
+ return req . body [ fieldname ] = val ;
58
+
59
+ if ( Array . isArray ( prev ) )
60
+ return prev . push ( val ) ;
61
+
62
+ req . body [ fieldname ] = [ prev , val ] ;
63
+ } ) ;
64
+
65
+ // Build req.files fields
66
+ busboy . on ( 'file' , function ( fieldname , file , filename , encoding , mime ) {
67
+ var buf = new Buffer ( 0 ) ;
68
+ var safeFileNameRegex = / [ ^ \w - ] / g;
69
+
70
+ file . on ( 'data' , function ( data ) {
71
+ buf = Buffer . concat ( [ buf , data ] ) ;
72
+
73
+ if ( options . debug )
74
+ return console . log ( 'Uploading %s -> %s' , fieldname , filename ) ;
75
+ } ) ;
76
+
77
+ file . on ( 'end' , function ( ) {
78
+ if ( ! req . files )
79
+ req . files = { } ;
80
+
81
+ // see: https://github.com/richardgirges/express-fileupload/issues/14
82
+ // firefox uploads empty file in case of cache miss when f5ing page.
83
+ // resulting in unexpected behavior. if there is no file data, the file is invalid.
84
+ if ( ! buf . length )
85
+ return ;
86
+
87
+ if ( options . safeFileNames ) {
88
+ if ( typeof options . safeFileNames === 'object' )
89
+ safeFileNameRegex = options . safeFileNames ;
90
+
91
+ filename = filename . replace ( safeFileNameRegex , '' ) ;
92
+ }
93
+
94
+ var newFile = {
95
+ name : filename ,
96
+ data : buf ,
97
+ encoding : encoding ,
98
+ mimetype : mime ,
99
+ mv : function ( path , callback ) {
100
+ var fstream = fs . createWriteStream ( path ) ;
101
+
102
+ streamifier . createReadStream ( buf ) . pipe ( fstream ) ;
103
+
104
+ fstream . on ( 'error' , function ( error ) {
105
+ if ( callback )
106
+ callback ( error ) ;
107
+ } ) ;
108
+
109
+ fstream . on ( 'close' , function ( ) {
110
+ if ( callback )
111
+ callback ( null ) ;
112
+ } ) ;
113
+ }
114
+ } ;
115
+
116
+ // Non-array fields
117
+ if ( ! req . files . hasOwnProperty ( fieldname ) ) {
118
+ req . files [ fieldname ] = newFile ;
119
+ } else {
120
+ // Array fields
121
+ if ( req . files [ fieldname ] instanceof Array )
122
+ req . files [ fieldname ] . push ( newFile ) ;
123
+ else
124
+ req . files [ fieldname ] = [ req . files [ fieldname ] , newFile ] ;
125
+ }
126
+ } ) ;
127
+ } ) ;
128
+
129
+ busboy . on ( 'finish' , next ) ;
130
+
131
+ req . pipe ( busboy ) ;
132
+ }
133
+
134
+
135
+ /**************************************************************
136
+ * Methods below were copied from, or heavily inspired by
137
+ * the Connect and connect-busboy packages
138
+ **************************************************************/
139
+
140
+ /**
141
+ * Ensures the request is not using a non-compliant multipart method
142
+ * such as GET or HEAD
143
+ * @param {Object } req Express req object
144
+ * @return {Boolean }
145
+ */
146
+ function hasAcceptableMethod ( req ) {
147
+ return ( UNACCEPTABLE_METHODS . indexOf ( req . method ) < 0 ) ;
148
+ }
149
+
150
+ /**
151
+ * Ensures that only multipart requests are processed by express-fileupload
152
+ * @param {Object } req Express req object
153
+ * @return {Boolean }
154
+ */
155
+ function hasAcceptableMime ( req ) {
156
+ var str = ( req . headers [ 'content-type' ] || '' ) . split ( ';' ) [ 0 ] ;
157
+
158
+ return ACCEPTABLE_MIME . test ( str ) ;
159
+ }
160
+
161
+ /**
162
+ * Ensures the request contains a content body
163
+ * @param {Object } req Express req object
164
+ * @return {Boolean }
165
+ */
166
+ function hasBody ( req ) {
167
+ return ( 'transfer-encoding' in req . headers ) ||
168
+ ( 'content-length' in req . headers && req . headers [ 'content-length' ] !== '0' ) ;
169
+ }
0 commit comments