@@ -4,7 +4,18 @@ var EventEmitter = require('events').EventEmitter,
4
4
formidable = require ( 'formidable' ) ,
5
5
imageMagick = require ( 'imagemagick' ) ,
6
6
mkdirp = require ( 'mkdirp' ) ,
7
+ async = require ( 'async' ) ,
7
8
_ = require ( 'lodash' ) ;
9
+
10
+ var convertArgs = [
11
+ 'srcPath' , 'srcData' , 'srcFormat' ,
12
+ 'dstPath' , 'quality' , 'format' ,
13
+ 'progressive' , 'colorspace' ,
14
+ 'width' , 'height' ,
15
+ 'strip' , 'filter' ,
16
+ 'sharpening' , 'customArgs' ,
17
+ 'timeout' , 'gravity'
18
+ ] ;
8
19
9
20
module . exports = function ( options ) {
10
21
@@ -59,17 +70,19 @@ module.exports = function (options) {
59
70
tmpFiles = [ ] ,
60
71
files = [ ] ,
61
72
map = { } ,
62
- counter = 1 ,
63
73
redirect ,
64
- finish = _ . bind ( function ( ) {
74
+ counter = 1 ,
75
+ finish = function ( ) {
65
76
if ( ! -- counter ) {
77
+ var data = [ ] ;
66
78
_ . each ( files , function ( fileInfo ) {
67
- this . initUrls ( fileInfo ) ;
79
+ this . initUrls ( fileInfo , true ) ;
68
80
this . emit ( 'end' , fileInfo ) ;
81
+ data . push ( fileInfo . toResponse ( ) ) ;
69
82
} , this ) ;
70
- this . callback ( files , redirect ) ;
83
+ this . callback ( data , files , redirect ) ;
71
84
}
72
- } , this ) ;
85
+ } . bind ( this ) ;
73
86
74
87
this . noCache ( ) ;
75
88
@@ -89,51 +102,36 @@ module.exports = function (options) {
89
102
}
90
103
} )
91
104
. on ( 'file' , function ( name , file ) {
92
- var fileInfo = map [ path . basename ( file . path ) ] ;
105
+ var mapKey = path . basename ( file . path ) ;
106
+ var fileInfo = map [ mapKey ] ;
93
107
if ( fs . existsSync ( file . path ) ) {
94
108
fileInfo . size = file . size ;
95
109
if ( ! fileInfo . validate ( ) ) {
96
110
fs . unlink ( file . path ) ;
97
111
return ;
112
+ } else {
113
+ counter ++ ;
98
114
}
99
-
100
- var generatePreviews = function ( ) {
101
- if ( options . imageTypes . test ( fileInfo . name ) ) {
102
- _ . each ( options . imageVersions , function ( value , version ) {
103
- // creating directory recursive
104
- if ( ! fs . existsSync ( options . uploadDir ( ) + '/' + version + '/' ) )
105
- mkdirp . sync ( options . uploadDir ( ) + '/' + version + '/' ) ;
106
-
107
- counter ++ ;
108
- var opts = options . imageVersions [ version ] ;
109
- imageMagick . resize ( {
110
- width : opts . width ,
111
- height : opts . height ,
112
- srcPath : options . uploadDir ( ) + '/' + fileInfo . name ,
113
- dstPath : options . uploadDir ( ) + '/' + version + '/' + fileInfo . name ,
114
- customArgs : opts . imageArgs || [ '-auto-orient' ]
115
- } , finish ) ;
116
- } ) ;
117
- }
115
+
116
+ var handledFile = function ( err , fileInfo , processedFiles ) {
117
+ fileInfo . processedFiles = processedFiles || [ ] ;
118
+ finish ( ) ;
118
119
}
119
120
120
- if ( ! fs . existsSync ( options . uploadDir ( ) + '/' ) )
121
- mkdirp . sync ( options . uploadDir ( ) + '/' ) ;
122
-
123
- counter ++ ;
121
+ if ( ! fs . existsSync ( options . uploadDir ( ) + '/' ) ) mkdirp . sync ( options . uploadDir ( ) + '/' ) ;
122
+
124
123
fs . rename ( file . path , options . uploadDir ( ) + '/' + fileInfo . name , function ( err ) {
125
124
if ( ! err ) {
126
- generatePreviews ( ) ;
127
- finish ( ) ;
125
+ self . processFile ( fileInfo , handledFile ) ;
128
126
} else {
129
127
var is = fs . createReadStream ( file . path ) ;
130
128
var os = fs . createWriteStream ( options . uploadDir ( ) + '/' + fileInfo . name ) ;
131
129
is . on ( 'end' , function ( err ) {
132
130
if ( ! err ) {
133
131
fs . unlinkSync ( file . path ) ;
134
- generatePreviews ( ) ;
132
+ return self . processFile ( fileInfo , handledFile ) ;
135
133
}
136
- finish ( ) ;
134
+ handledFile ( fileInfo , [ ] ) ;
137
135
} ) ;
138
136
is . pipe ( os ) ;
139
137
}
@@ -178,16 +176,117 @@ module.exports = function (options) {
178
176
}
179
177
} ;
180
178
181
- UploadHandler . prototype . initUrls = function ( fileInfo ) {
179
+ UploadHandler . prototype . initUrls = function ( fileInfo , noCheck ) {
182
180
var baseUrl = ( options . ssl ? 'https:' : 'http:' ) + '//' + ( options . hostname || this . req . get ( 'Host' ) ) ;
183
181
fileInfo . setUrl ( null , baseUrl + options . uploadUrl ( ) ) ;
184
182
fileInfo . setUrl ( 'delete' , baseUrl + this . req . originalUrl ) ;
185
183
_ . each ( options . imageVersions , function ( value , version ) {
186
- if ( fs . existsSync ( options . uploadDir ( ) + '/' + version + '/' + fileInfo . name ) ) {
184
+ if ( noCheck || fs . existsSync ( options . uploadDir ( ) + '/' + version + '/' + fileInfo . name ) ) {
187
185
fileInfo . setUrl ( version , baseUrl + options . uploadUrl ( ) + '/' + version ) ;
188
186
}
189
187
} , this ) ;
190
188
} ;
189
+
190
+ UploadHandler . prototype . processFile = function ( fileInfo , processOpts , callback ) {
191
+ if ( _ . isFunction ( processOpts ) ) {
192
+ callback = processOpts ;
193
+ processOpts = _ . extend ( { } , options ) ; // use global options
194
+ }
195
+ var self = this ;
196
+ var files = [ ] ;
197
+ var uploadDir = _ . result ( processOpts , 'uploadDir' ) ;
198
+ var srcPath = uploadDir + '/' + fileInfo . name ;
199
+ var isImage = processOpts . imageTypes && processOpts . imageTypes . test ( fileInfo . name ) ;
200
+ var commands = [ ] ;
201
+ fileInfo . metadata = { } ;
202
+
203
+ // File metadata
204
+ if ( processOpts . identify ) {
205
+ if ( isImage ) {
206
+ commands . push ( function ( next ) {
207
+ imageMagick . identify ( srcPath , function ( err , features ) {
208
+ fileInfo . metadata = err ? { } : features ;
209
+ fileInfo . metadata . fromOriginal = true ;
210
+ next ( ) ;
211
+ } ) ;
212
+ } ) ;
213
+ } // could add generic file identify fn here
214
+ }
215
+
216
+ // Generic processing, after images have been processed
217
+ _ . each ( [ ] . concat ( processOpts . process || [ ] ) , function ( cmd ) {
218
+ commands . push ( function ( next ) {
219
+ cmd . call ( null , fileInfo , srcPath , function ( err , result ) {
220
+ var info = _ . extend ( { } , fileInfo , { srcPath : srcPath , result : result } ) ;
221
+ if ( err && ! info . error ) info . error = err ;
222
+ if ( _ . isObject ( result ) && result instanceof FileInfo ) {
223
+ files . push ( result ) ;
224
+ }
225
+ next ( info . error ) ;
226
+ } ) ;
227
+ } ) ;
228
+ } ) ;
229
+
230
+ // Image processing
231
+ if ( isImage ) {
232
+ commands . push ( function ( next ) {
233
+ async . mapSeries ( _ . keys ( processOpts . imageVersions || { } ) , function ( version , done ) {
234
+ var identify = processOpts . identify ;
235
+ var dstPath = uploadDir + '/' + version + '/' + fileInfo . name ;
236
+ var cb = function ( err ) {
237
+ var args = arguments ;
238
+ var info = _ . extend ( { } , fileInfo , {
239
+ srcPath : srcPath , dstPath : dstPath , version : version
240
+ } ) ;
241
+ if ( err ) info . error = err ;
242
+ if ( ! err && identify ) {
243
+ imageMagick . identify ( dstPath , function ( err , features ) {
244
+ info . metadata = err ? { } : features ;
245
+ info . metadata . fromOriginal = false ;
246
+ files . push ( info ) ;
247
+ done . apply ( null , args ) ;
248
+ } ) ;
249
+ } else {
250
+ files . push ( info ) ;
251
+ done . apply ( null , args ) ;
252
+ }
253
+ } ;
254
+
255
+ var process = function ( err ) {
256
+ if ( err ) return cb ( err ) ;
257
+ var opts = processOpts . imageVersions [ version ] || { } ;
258
+ if ( _ . isObject ( fileInfo . error ) ) {
259
+ cb ( fileInfo . error ) ;
260
+ } else if ( _ . isFunction ( opts ) ) {
261
+ opts . call ( imageMagick , fileInfo , srcPath , dstPath , cb ) ;
262
+ } else if ( _ . isArray ( opts ) ) { // raw imagemagick convert
263
+ imageMagick . convert ( opts , cb ) ;
264
+ } else if ( _ . isObject ( opts ) ) {
265
+ identify = ( identify || opts . identify ) && opts . identify !== false ;
266
+ var m = opts . crop ? 'crop' : 'resize' ;
267
+ var args = _ . pick ( opts , convertArgs ) ;
268
+ args . srcPath = args . srcPath || srcPath ;
269
+ args . dstPath = args . dstPath || dstPath ;
270
+ args . customArgs = args . customArgs || opts . imageArgs || [ '-auto-orient' ] ;
271
+ imageMagick [ m ] ( args , cb ) ;
272
+ } else {
273
+ cb ( new Error ( 'Invalid image version config: ' + version ) ) ;
274
+ }
275
+ }
276
+
277
+ var versionDir = uploadDir + '/' + version + '/' ;
278
+ fs . exists ( versionDir , function ( exists ) {
279
+ exists ? process ( ) : mkdirp ( versionDir , process ) ;
280
+ } ) ;
281
+ } , next ) ;
282
+ } ) ;
283
+ }
284
+
285
+ async . series ( commands , function ( err ) {
286
+ if ( ! err ) self . emit ( 'processed' , fileInfo , files ) ;
287
+ callback ( err , fileInfo , files ) ;
288
+ } ) ;
289
+ } ;
191
290
192
291
return UploadHandler ;
193
292
}
0 commit comments