@@ -4,7 +4,18 @@ var EventEmitter = require('events').EventEmitter,
44 formidable = require ( 'formidable' ) ,
55 imageMagick = require ( 'imagemagick' ) ,
66 mkdirp = require ( 'mkdirp' ) ,
7+ async = require ( 'async' ) ,
78 _ = 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+ ] ;
819
920module . exports = function ( options ) {
1021
@@ -59,17 +70,19 @@ module.exports = function (options) {
5970 tmpFiles = [ ] ,
6071 files = [ ] ,
6172 map = { } ,
62- counter = 1 ,
6373 redirect ,
64- finish = _ . bind ( function ( ) {
74+ counter = 1 ,
75+ finish = function ( ) {
6576 if ( ! -- counter ) {
77+ var data = [ ] ;
6678 _ . each ( files , function ( fileInfo ) {
67- this . initUrls ( fileInfo ) ;
79+ this . initUrls ( fileInfo , true ) ;
6880 this . emit ( 'end' , fileInfo ) ;
81+ data . push ( fileInfo . toResponse ( ) ) ;
6982 } , this ) ;
70- this . callback ( files , redirect ) ;
83+ this . callback ( data , files , redirect ) ;
7184 }
72- } , this ) ;
85+ } . bind ( this ) ;
7386
7487 this . noCache ( ) ;
7588
@@ -89,51 +102,36 @@ module.exports = function (options) {
89102 }
90103 } )
91104 . 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 ] ;
93107 if ( fs . existsSync ( file . path ) ) {
94108 fileInfo . size = file . size ;
95109 if ( ! fileInfo . validate ( ) ) {
96110 fs . unlink ( file . path ) ;
97111 return ;
112+ } else {
113+ counter ++ ;
98114 }
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 ( ) ;
118119 }
119120
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+
124123 fs . rename ( file . path , options . uploadDir ( ) + '/' + fileInfo . name , function ( err ) {
125124 if ( ! err ) {
126- generatePreviews ( ) ;
127- finish ( ) ;
125+ self . processFile ( fileInfo , handledFile ) ;
128126 } else {
129127 var is = fs . createReadStream ( file . path ) ;
130128 var os = fs . createWriteStream ( options . uploadDir ( ) + '/' + fileInfo . name ) ;
131129 is . on ( 'end' , function ( err ) {
132130 if ( ! err ) {
133131 fs . unlinkSync ( file . path ) ;
134- generatePreviews ( ) ;
132+ return self . processFile ( fileInfo , handledFile ) ;
135133 }
136- finish ( ) ;
134+ handledFile ( fileInfo , [ ] ) ;
137135 } ) ;
138136 is . pipe ( os ) ;
139137 }
@@ -178,16 +176,117 @@ module.exports = function (options) {
178176 }
179177 } ;
180178
181- UploadHandler . prototype . initUrls = function ( fileInfo ) {
179+ UploadHandler . prototype . initUrls = function ( fileInfo , noCheck ) {
182180 var baseUrl = ( options . ssl ? 'https:' : 'http:' ) + '//' + ( options . hostname || this . req . get ( 'Host' ) ) ;
183181 fileInfo . setUrl ( null , baseUrl + options . uploadUrl ( ) ) ;
184182 fileInfo . setUrl ( 'delete' , baseUrl + this . req . originalUrl ) ;
185183 _ . 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 ) ) {
187185 fileInfo . setUrl ( version , baseUrl + options . uploadUrl ( ) + '/' + version ) ;
188186 }
189187 } , this ) ;
190188 } ;
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+ } ;
191290
192291 return UploadHandler ;
193292}
0 commit comments