1- var busboy = require ( 'connect- busboy' ) ;
1+ var Busboy = require ( 'busboy' ) ;
22var fs = require ( 'fs-extra' ) ;
33var streamifier = require ( 'streamifier' ) ;
44
5+ var ACCEPTABLE_MIME = / ^ (?: m u l t i p a r t \/ .+ ) $ / i;
6+ var UNACCEPTABLE_METHODS = [
7+ 'GET' ,
8+ 'HEAD'
9+ ] ;
10+
511module . exports = function ( options ) {
612 options = options || { } ;
713
814 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 ) )
1316 return next ( ) ;
1417
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 ) ;
9419 } ;
9520} ;
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