Skip to content

Commit 5d4f826

Browse files
committed
Add checks if file exists on unlink
1 parent df35fd8 commit 5d4f826

File tree

1 file changed

+270
-49
lines changed

1 file changed

+270
-49
lines changed

lib/filehandler.js

Lines changed: 270 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,278 @@
1-
module.exports = function (middleware, options) {
1+
var EventEmitter = require('events').EventEmitter,
2+
path = require('path'),
3+
fs = require('fs'),
4+
formidable = require('formidable'),
5+
imageMagick = require('imagemagick'),
6+
mkdirp = require('mkdirp'),
7+
_ = require('lodash'),
8+
async = require('async');
29

3-
return function (req, res, next) {
4-
res.set({
5-
'Access-Control-Allow-Origin': options.accessControl.allowOrigin,
6-
'Access-Control-Allow-Methods': options.accessControl.allowMethods
10+
module.exports = function (options) {
11+
12+
var FileInfo = require('./fileinfo')(
13+
_.extend({
14+
baseDir: options.uploadDir
15+
}, _.pick(options, 'minFileSize', 'maxFileSize', 'acceptFileTypes'))
16+
);
17+
18+
var UploadHandler = function (req, res, callback) {
19+
EventEmitter.call(this);
20+
this.req = req;
21+
this.res = res;
22+
this.callback = callback;
23+
};
24+
require('util').inherits(UploadHandler, EventEmitter);
25+
26+
UploadHandler.prototype.noCache = function () {
27+
this.res.set({
28+
'Pragma': 'no-cache',
29+
'Cache-Control': 'no-store, no-cache, must-revalidate'
730
});
8-
var UploadHandler = require('./uploadhandler')(options);
9-
var handler = new UploadHandler(req, res, function (result, redirect) {
10-
if (redirect) {
11-
files = {files: result};
12-
res.redirect(redirect.replace(/%s/, encodeURIComponent(JSON.stringify(files))));
31+
if ((this.req.get("Accept") || "").indexOf("application/json") != -1) {
32+
this.res.set({
33+
'Content-Type': 'application/json',
34+
'Content-Disposition': 'inline; filename="files.json"'
35+
});
36+
} else {
37+
this.res.set({ 'Content-Type': 'text/plain' });
38+
}
39+
};
40+
41+
UploadHandler.prototype.get = function () {
42+
this.noCache();
43+
var files = [];
44+
fs.readdir(options.uploadDir(), _.bind(function (err, list) {
45+
async.each(list, _.bind(function(name, cb) {
46+
fs.stat(options.uploadDir() + '/' + name, _.bind(function(err, stats) {
47+
if (!err && stats.isFile()) {
48+
var fileInfo = new FileInfo({
49+
name: name,
50+
size: stats.size
51+
});
52+
this.initUrls(fileInfo, function(err) {
53+
files.push(fileInfo);
54+
cb(err);
55+
});
56+
}
57+
else cb(err);
58+
}, this));
59+
}, this),
60+
_.bind(function(err) {
61+
this.callback({files: files});
62+
}, this));
63+
}, this));
64+
};
65+
66+
UploadHandler.prototype.post = function () {
67+
var self = this,
68+
form = new formidable.IncomingForm(),
69+
tmpFiles = [],
70+
files = [],
71+
map = {},
72+
counter = 1,
73+
redirect,
74+
finish = _.bind(function () {
75+
if (!--counter) {
76+
async.each(files, _.bind(function(fileInfo, cb) {
77+
this.initUrls(fileInfo, _.bind(function(err) {
78+
this.emit('end', fileInfo);
79+
cb(err);
80+
}, this));
81+
}, this),
82+
_.bind(function(err) {
83+
this.callback({files: files}, redirect);
84+
}, this));
85+
}
86+
}, this);
87+
88+
this.noCache();
89+
90+
form.uploadDir = options.tmpDir;
91+
form
92+
.on('fileBegin', function (name, file) {
93+
tmpFiles.push(file.path);
94+
var fileInfo = new FileInfo(file);
95+
fileInfo.safeName();
96+
map[path.basename(file.path)] = fileInfo;
97+
files.push(fileInfo);
98+
self.emit('begin', fileInfo);
99+
})
100+
.on('field', function (name, value) {
101+
if (name === 'redirect') {
102+
redirect = value;
103+
}
104+
if ( !self.req.fields )
105+
self.req.fields = {};
106+
self.req.fields[name] = value;
107+
})
108+
.on('file', function (name, file) {
109+
counter++;
110+
var fileInfo = map[path.basename(file.path)];
111+
fs.exists(file.path, function(exists) {
112+
if (exists) {
113+
fileInfo.size = file.size;
114+
if (!fileInfo.validate()) {
115+
//fs.unlink(file.path);
116+
117+
fs.unlink(file.path, function(err) {
118+
if(err && err.code == 'ENOENT') {
119+
// file doens't exist
120+
console.info("File doesn't exist, won't remove it.");
121+
} else if (err) {
122+
// other errors, e.g. maybe we don't have enough permission
123+
console.error("Error occurred while trying to remove file");
124+
} else {
125+
console.info(`removed`);
126+
}
127+
});
128+
129+
finish();
130+
return;
131+
}
132+
133+
var generatePreviews = function () {
134+
if (options.imageTypes.test(fileInfo.name)) {
135+
_.each(options.imageVersions, function (value, version) {
136+
counter++;
137+
// creating directory recursive
138+
mkdirp(options.uploadDir() + '/' + version + '/', function (err, made) {
139+
var opts = options.imageVersions[version];
140+
imageMagick.resize({
141+
width: opts.width,
142+
height: opts.height,
143+
srcPath: options.uploadDir() + '/' + fileInfo.name,
144+
dstPath: options.uploadDir() + '/' + version + '/' + fileInfo.name,
145+
customArgs: opts.imageArgs || ['-auto-orient']
146+
}, finish);
147+
});
148+
});
149+
}
150+
}
151+
152+
mkdirp(options.uploadDir() + '/', function(err, made) {
153+
fs.rename(file.path, options.uploadDir() + '/' + fileInfo.name, function (err) {
154+
if (!err) {
155+
generatePreviews();
156+
finish();
157+
} else {
158+
var is = fs.createReadStream(file.path);
159+
var os = fs.createWriteStream(options.uploadDir() + '/' + fileInfo.name);
160+
is.on('end', function (err) {
161+
if (!err) {
162+
//fs.unlink(file.path);
163+
fs.unlink(file.path, function(err) {
164+
if(err && err.code == 'ENOENT') {
165+
// file doens't exist
166+
console.info("File doesn't exist, won't remove it.");
167+
} else if (err) {
168+
// other errors, e.g. maybe we don't have enough permission
169+
console.error("Error occurred while trying to remove file");
170+
} else {
171+
console.info(`removed`);
172+
}
173+
});
174+
175+
generatePreviews();
176+
}
177+
finish();
178+
});
179+
is.pipe(os);
180+
}
181+
});
182+
});
183+
}
184+
else finish();
185+
});
186+
})
187+
.on('aborted', function () {
188+
_.each(tmpFiles, function (file) {
189+
var fileInfo = map[path.basename(file)];
190+
self.emit('abort', fileInfo);
191+
//fs.unlinkSync(file);
192+
193+
fs.unlink(file, function(err) {
194+
if(err && err.code == 'ENOENT') {
195+
// file doens't exist
196+
console.info("File doesn't exist, won't remove it.");
197+
} else if (err) {
198+
// other errors, e.g. maybe we don't have enough permission
199+
console.error("Error occurred while trying to remove file");
200+
} else {
201+
console.info(`removed`);
202+
}
203+
});
204+
205+
});
206+
})
207+
.on('error', function (e) {
208+
self.emit('error', e);
209+
})
210+
.on('progress', function (bytesReceived, bytesExpected) {
211+
if (bytesReceived > options.maxPostSize)
212+
self.req.connection.destroy();
213+
})
214+
.on('end', finish)
215+
.parse(self.req);
216+
};
217+
218+
UploadHandler.prototype.destroy = function () {
219+
var self = this,
220+
fileName = path.basename(decodeURIComponent(this.req.url));
221+
222+
var filepath = path.join(options.uploadDir(), fileName);
223+
if (filepath.indexOf(options.uploadDir()) !== 0) {
224+
self.emit('delete', fileName);
225+
self.callback({success: false});
226+
return;
227+
}
228+
229+
230+
fs.unlink(filepath, function (ex) {
231+
if(ex && ex.code == 'ENOENT') {
232+
// file doens't exist
233+
console.info("File doesn't exist, won't remove it.");
234+
} else if (ex) {
235+
// other errors, e.g. maybe we don't have enough permission
236+
console.error("Error occurred while trying to remove file");
13237
} else {
14-
res.set({
15-
'Content-Type': (req.headers.accept || '').indexOf('application/json') !== -1
16-
? 'application/json'
17-
: 'text/plain'
238+
239+
_.each(options.imageVersions, function (value, version) {
240+
//fs.unlinkSync(path.join(options.uploadDir(), version, fileName));
241+
242+
fs.unlink(path.join(options.uploadDir(), version, fileName), function (err) {
243+
if (err && err.code == 'ENOENT') {
244+
// file doens't exist
245+
console.info("File doesn't exist, won't remove it.");
246+
} else if (err) {
247+
// other errors, e.g. maybe we don't have enough permission
248+
console.error("Error occurred while trying to remove file");
249+
} else {
250+
console.info(`removed`);
251+
}
252+
});
253+
18254
});
19-
res.status(200).json(result);
255+
256+
20257
}
21-
});
258+
self.emit('delete', fileName);
259+
self.callback({success: !ex});
22260

23-
handler.on('begin', function (fileInfo) {
24-
middleware.emit('begin', fileInfo, req, res);
25-
});
26-
handler.on('end', function (fileInfo) {
27-
middleware.emit('end', fileInfo, req, res);
28-
});
29-
handler.on('abort', function (fileInfo) {
30-
middleware.emit('abort', fileInfo, req, res);
31-
});
32-
handler.on('error', function (e) {
33-
middleware.emit('abort', e, req, res);
34-
});
35-
handler.on('delete', function (fileName) {
36-
middleware.emit('delete', fileName, req, res);
37261
});
262+
};
38263

39-
switch (req.method) {
40-
case 'OPTIONS':
41-
res.end();
42-
break;
43-
case 'HEAD':
44-
case 'GET':
45-
handler.get();
46-
break;
47-
case 'POST':
48-
handler.post();
49-
break;
50-
case 'DELETE':
51-
handler.destroy();
52-
break;
53-
default:
54-
res.send(405);
55-
}
56-
}
57-
};
264+
UploadHandler.prototype.initUrls = function (fileInfo, cb) {
265+
var baseUrl = (options.ssl ? 'https:' : 'http:') + '//' + (options.hostname || this.req.get('Host'));
266+
fileInfo.setUrl(null, baseUrl + options.uploadUrl());
267+
fileInfo.setUrl('delete', baseUrl + this.req.originalUrl);
268+
async.each(Object.keys(options.imageVersions), function(version, cb) {
269+
fs.exists(options.uploadDir() + '/' + version + '/' + fileInfo.name, function(exists) {
270+
if (exists) fileInfo.setUrl(version, baseUrl + options.uploadUrl() + '/' + version);
271+
cb(null);
272+
})
273+
},
274+
cb);
275+
};
276+
277+
return UploadHandler;
278+
}

0 commit comments

Comments
 (0)