diff --git a/History.md b/History.md index bbfd1b2..8a03709 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,44 @@ +0.1.7 / 2014-10-09 +================== + + * #58 improved support for IE9 + +0.1.6 / 2014-10-04 +================== + + * #56 avoid path traversal in DELETE requests + +0.1.5 / 2014-08-02 +================== + + * #31 request and response objects passed to event handlers + +0.1.4 / 2014-07-06 +================== + + * #53 documentation update + +0.1.3 / 2014-07-05 +================== + + * #46 merged in (reduce synchronous disk IO operations) + +0.1.2 / 2014-06-08 +================== + + * #31 merged in + +0.1.1 / 2014-02-04 +================== + + * #5 fixed + +0.1.0 / 2013-10-07 +================== + + * documentation cleanup + + 0.0.9 / 2013-10-07 ================== diff --git a/README.md b/README.md index 7110a2d..204c7a3 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,6 @@ jquery-file-upload-middleware ============================= -### Personal Patch Repository - -Please comeback [Aleksandr Guidrevitch](http://aguidrevitch.blogspot.com/) lol - -- use bower to install jquery-file-upload (planed) -- use not npm to install jquery-file-upload-middleware -- use this repository by - -```json -{ - "name": "your project", - "version": "0.1.0", - "private": true, - "dependencies": { - "express": "3.3.x", - "jquery-file-upload-middleware": "git://github.com/soomtong/jquery-file-upload-middleware.git", - "swig": "1.0.x", - "imagemagick": "0.1.x" - }, - "devDependencies": { - "nodeunit":"*" - } -} -``` - - - - - ---- - -## Readme will update later - jQuery-File-Upload Express.js middleware. Based on the server code of [jQuery-File-Upload](https://github.com/blueimp/jQuery-File-Upload) Installation: @@ -78,6 +45,38 @@ On the frontend: ``` +To prevent access to /upload except for post (for security) +```javascript +upload.configure({ + uploadDir: __dirname + '/public/uploads/', + uploadUrl: '/uploads' +}); + +/// Redirect all to home except post +app.get('/upload', function( req, res ){ + res.redirect('/'); +}); + +app.put('/upload', function( req, res ){ + res.redirect('/'); +}); + +app.delete('/upload', function( req, res ){ + res.redirect('/'); +}); + +app.use('/upload', function(req, res, next){ + upload.fileHandler({ + uploadDir: function () { + return __dirname + '/public/uploads/' + }, + uploadUrl: function () { + return '/uploads' + } + })(req, res, next); +}); +``` + Overriding global configuration ```javascript @@ -101,7 +100,7 @@ More sophisticated example - Events app.use('/upload', upload.fileHandler()); // events - upload.on('begin', function (fileInfo) { + upload.on('begin', function (fileInfo, req, res) { // fileInfo structure is the same as returned to browser // { // name: '3 (3).jpg', @@ -114,10 +113,10 @@ More sophisticated example - Events // thumbnail_url: 'http://youhost/uploads/thumbnail/3%20(3).jpg' // } }); - upload.on('abort', function (fileInfo) { ... }); - upload.on('end', function (fileInfo) { ... }); - upload.on('delete', function (fileInfo) { ... }); - upload.on('error', function (e) { + upload.on('abort', function (fileInfo, req, res) { ... }); + upload.on('end', function (fileInfo, req, res) { ... }); + upload.on('delete', function (fileInfo, req, res) { ... }); + upload.on('error', function (e, req, res) { console.log(e.message); }); ``` @@ -278,6 +277,10 @@ Other options and their default values: ## Contributors * [@soomtong](http://github.com/soomtong) + * [@gsarwohadi](https://github.com/gsarwohadi) + * [@peecky](https://github.com/peecky) + * [@tonyspiro](https://github.com/tonyspiro) + * [@derjust](https://github.com/derjust) ## License Copyright (c) 2012 [Aleksandr Guidrevitch](http://aguidrevitch.blogspot.com/) diff --git a/index.js b/index.js index 1488b5f..0ebaf48 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ var _ = require('lodash'), - EventEmitter = require('events').EventEmitter; + EventEmitter = require('events').EventEmitter, + os = require("os"); var JqueryFileUploadMiddleware = function () { EventEmitter.call(this); @@ -10,7 +11,7 @@ require('util').inherits(JqueryFileUploadMiddleware, EventEmitter); JqueryFileUploadMiddleware.prototype.prepareOptions = function (options) { options = _.extend({ - tmpDir: '/tmp', + tmpDir: os.tmpdir(), uploadDir: __dirname + '/public/files', uploadUrl: '/files/', maxPostSize: 11000000000, // 11 GB diff --git a/lib/filehandler.js b/lib/filehandler.js index bab6f89..e7e227a 100644 --- a/lib/filehandler.js +++ b/lib/filehandler.js @@ -16,24 +16,24 @@ module.exports = function (middleware, options) { ? 'application/json' : 'text/plain' }); - res.json(200, result); + res.status(200).json(result); } }); handler.on('begin', function (fileInfo) { - middleware.emit('begin', fileInfo); + middleware.emit('begin', fileInfo, req, res); }); handler.on('end', function (fileInfo) { - middleware.emit('end', fileInfo); + middleware.emit('end', fileInfo, req, res); }); handler.on('abort', function (fileInfo) { - middleware.emit('abort', fileInfo); + middleware.emit('abort', fileInfo, req, res); }); handler.on('error', function (e) { - middleware.emit('abort', e); + middleware.emit('abort', e, req, res); }); handler.on('delete', function (fileName) { - middleware.emit('delete', fileName); + middleware.emit('delete', fileName, req, res); }); switch (req.method) { diff --git a/lib/uploadhandler.js b/lib/uploadhandler.js index ece4b55..070f018 100644 --- a/lib/uploadhandler.js +++ b/lib/uploadhandler.js @@ -4,7 +4,8 @@ var EventEmitter = require('events').EventEmitter, formidable = require('formidable'), imageMagick = require('imagemagick'), mkdirp = require('mkdirp'), - _ = require('lodash'); + _ = require('lodash'), + async = require('async'); module.exports = function (options) { @@ -25,28 +26,40 @@ module.exports = function (options) { UploadHandler.prototype.noCache = function () { this.res.set({ 'Pragma': 'no-cache', - 'Cache-Control': 'no-store, no-cache, must-revalidate', - 'Content-Disposition': 'inline; filename="files.json"' + 'Cache-Control': 'no-store, no-cache, must-revalidate' }); + if ((this.req.get("Accept") || "").indexOf("application/json") != -1) { + this.res.set({ + 'Content-Type': 'application/json', + 'Content-Disposition': 'inline; filename="files.json"' + }); + } else { + this.res.set({ 'Content-Type': 'text/plain' }); + } }; UploadHandler.prototype.get = function () { this.noCache(); var files = []; fs.readdir(options.uploadDir(), _.bind(function (err, list) { - _.each(list, function (name) { - var stats = fs.statSync(options.uploadDir() + '/' + name), - fileInfo; - if (stats.isFile()) { - fileInfo = new FileInfo({ - name: name, - size: stats.size - }); - this.initUrls(fileInfo); - files.push(fileInfo); - } - }, this); - this.callback({files: files}); + async.each(list, _.bind(function(name, cb) { + fs.stat(options.uploadDir() + '/' + name, _.bind(function(err, stats) { + if (!err && stats.isFile()) { + var fileInfo = new FileInfo({ + name: name, + size: stats.size + }); + this.initUrls(fileInfo, function(err) { + files.push(fileInfo); + cb(err); + }); + } + else cb(err); + }, this)); + }, this), + _.bind(function(err) { + this.callback({files: files}); + }, this)); }, this)); }; @@ -60,11 +73,15 @@ module.exports = function (options) { redirect, finish = _.bind(function () { if (!--counter) { - _.each(files, function (fileInfo) { - this.initUrls(fileInfo); - this.emit('end', fileInfo); - }, this); - this.callback({files: files}, redirect); + async.each(files, _.bind(function(fileInfo, cb) { + this.initUrls(fileInfo, _.bind(function(err) { + this.emit('end', fileInfo); + cb(err); + }, this)); + }, this), + _.bind(function(err) { + this.callback({files: files}, redirect); + }, this)); } }, this); @@ -84,58 +101,63 @@ module.exports = function (options) { if (name === 'redirect') { redirect = value; } + if ( !self.req.fields ) + self.req.fields = {}; + self.req.fields[name] = value; }) .on('file', function (name, file) { + counter++; var fileInfo = map[path.basename(file.path)]; - if (fs.existsSync(file.path)) { - fileInfo.size = file.size; - if (!fileInfo.validate()) { - fs.unlink(file.path); - return; - } - - var generatePreviews = function () { - if (options.imageTypes.test(fileInfo.name)) { - _.each(options.imageVersions, function (value, version) { - // creating directory recursive - if (!fs.existsSync(options.uploadDir() + '/' + version + '/')) - mkdirp.sync(options.uploadDir() + '/' + version + '/'); - - counter++; - var opts = options.imageVersions[version]; - imageMagick.resize({ - width: opts.width, - height: opts.height, - srcPath: options.uploadDir() + '/' + fileInfo.name, - dstPath: options.uploadDir() + '/' + version + '/' + fileInfo.name, - customArgs: opts.imageArgs || ['-auto-orient'] - }, finish); - }); + fs.exists(file.path, function(exists) { + if (exists) { + fileInfo.size = file.size; + if (!fileInfo.validate()) { + fs.unlink(file.path); + finish(); + return; } - } - if (!fs.existsSync(options.uploadDir() + '/')) - mkdirp.sync(options.uploadDir() + '/'); + var generatePreviews = function () { + if (options.imageTypes.test(fileInfo.name)) { + _.each(options.imageVersions, function (value, version) { + counter++; + // creating directory recursive + mkdirp(options.uploadDir() + '/' + version + '/', function (err, made) { + var opts = options.imageVersions[version]; + imageMagick.resize({ + width: opts.width, + height: opts.height, + srcPath: options.uploadDir() + '/' + fileInfo.name, + dstPath: options.uploadDir() + '/' + version + '/' + fileInfo.name, + customArgs: opts.imageArgs || ['-auto-orient'] + }, finish); + }); + }); + } + } - counter++; - fs.rename(file.path, options.uploadDir() + '/' + fileInfo.name, function (err) { - if (!err) { - generatePreviews(); - finish(); - } else { - var is = fs.createReadStream(file.path); - var os = fs.createWriteStream(options.uploadDir() + '/' + fileInfo.name); - is.on('end', function (err) { + mkdirp(options.uploadDir() + '/', function(err, made) { + fs.rename(file.path, options.uploadDir() + '/' + fileInfo.name, function (err) { if (!err) { - fs.unlinkSync(file.path); generatePreviews(); + finish(); + } else { + var is = fs.createReadStream(file.path); + var os = fs.createWriteStream(options.uploadDir() + '/' + fileInfo.name); + is.on('end', function (err) { + if (!err) { + fs.unlink(file.path); + generatePreviews(); + } + finish(); + }); + is.pipe(os); } - finish(); }); - is.pipe(os); - } - }); - } + }); + } + else finish(); + }); }) .on('aborted', function () { _.each(tmpFiles, function (file) { @@ -159,24 +181,32 @@ module.exports = function (options) { var self = this, fileName = path.basename(decodeURIComponent(this.req.url)); - fs.unlink(options.uploadDir() + '/' + fileName, function (ex) { + var filepath = path.join(options.uploadDir(), fileName); + if (filepath.indexOf(options.uploadDir()) !== 0) { + self.emit('delete', fileName); + self.callback({success: false}); + return; + } + fs.unlink(filepath, function (ex) { _.each(options.imageVersions, function (value, version) { - fs.unlink(options.uploadDir() + '/' + version + '/' + fileName); + fs.unlink(path.join(options.uploadDir(), version, fileName)); }); self.emit('delete', fileName); self.callback({success: !ex}); }); }; - UploadHandler.prototype.initUrls = function (fileInfo) { + UploadHandler.prototype.initUrls = function (fileInfo, cb) { var baseUrl = (options.ssl ? 'https:' : 'http:') + '//' + (options.hostname || this.req.get('Host')); fileInfo.setUrl(null, baseUrl + options.uploadUrl()); fileInfo.setUrl('delete', baseUrl + this.req.originalUrl); - _.each(options.imageVersions, function (value, version) { - if (fs.existsSync(options.uploadDir() + '/' + version + '/' + fileInfo.name)) { - fileInfo.setUrl(version, baseUrl + options.uploadUrl() + '/' + version); - } - }, this); + async.each(Object.keys(options.imageVersions), function(version, cb) { + fs.exists(options.uploadDir() + '/' + version + '/' + fileInfo.name, function(exists) { + if (exists) fileInfo.setUrl(version, baseUrl + options.uploadUrl() + '/' + version); + cb(null); + }) + }, + cb); }; return UploadHandler; diff --git a/package.json b/package.json index 722e4e8..dc11e0c 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,13 @@ "express", "middleware" ], - "version": "0.0.9", + "version": "0.1.7", "dependencies": { "formidable": ">=1.0.11", "imagemagick": ">=0.1.2", "lodash": ">= 0.9.2", - "mkdirp": ">= 0.3.4" + "mkdirp": ">= 0.3.4", + "async": "*" }, "engines": { "node": ">= 0.8.8" @@ -28,6 +29,6 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "_id": "jquery-file-upload-middleware@0.0.9", + "_id": "jquery-file-upload-middleware@0.1.7", "license": "MIT" }