| 21627 |
kshitij.so |
1 |
#!/usr/bin/env node
|
|
|
2 |
/*
|
|
|
3 |
* jQuery File Upload Plugin Node.js Example 2.0
|
|
|
4 |
* https://github.com/blueimp/jQuery-File-Upload
|
|
|
5 |
*
|
|
|
6 |
* Copyright 2012, Sebastian Tschan
|
|
|
7 |
* https://blueimp.net
|
|
|
8 |
*
|
|
|
9 |
* Licensed under the MIT license:
|
|
|
10 |
* http://www.opensource.org/licenses/MIT
|
|
|
11 |
*/
|
|
|
12 |
|
|
|
13 |
/*jslint nomen: true, regexp: true, unparam: true, stupid: true */
|
|
|
14 |
/*global require, __dirname, unescape, console */
|
|
|
15 |
|
|
|
16 |
(function (port) {
|
|
|
17 |
'use strict';
|
|
|
18 |
var path = require('path'),
|
|
|
19 |
fs = require('fs'),
|
|
|
20 |
// Since Node 0.8, .existsSync() moved from path to fs:
|
|
|
21 |
_existsSync = fs.existsSync || path.existsSync,
|
|
|
22 |
formidable = require('formidable'),
|
|
|
23 |
nodeStatic = require('node-static'),
|
|
|
24 |
imageMagick = require('imagemagick'),
|
|
|
25 |
options = {
|
|
|
26 |
tmpDir: __dirname + '/tmp',
|
|
|
27 |
publicDir: __dirname + '/public',
|
|
|
28 |
uploadDir: __dirname + '/public/files',
|
|
|
29 |
uploadUrl: '/files/',
|
|
|
30 |
maxPostSize: 11000000000, // 11 GB
|
|
|
31 |
minFileSize: 1,
|
|
|
32 |
maxFileSize: 10000000000, // 10 GB
|
|
|
33 |
acceptFileTypes: /.+/i,
|
|
|
34 |
// Files not matched by this regular expression force a download dialog,
|
|
|
35 |
// to prevent executing any scripts in the context of the service domain:
|
|
|
36 |
safeFileTypes: /\.(gif|jpe?g|png)$/i,
|
|
|
37 |
imageTypes: /\.(gif|jpe?g|png)$/i,
|
|
|
38 |
imageVersions: {
|
|
|
39 |
'thumbnail': {
|
|
|
40 |
width: 80,
|
|
|
41 |
height: 80
|
|
|
42 |
}
|
|
|
43 |
},
|
|
|
44 |
accessControl: {
|
|
|
45 |
allowOrigin: '*',
|
|
|
46 |
allowMethods: 'OPTIONS, HEAD, GET, POST, PUT, DELETE'
|
|
|
47 |
},
|
|
|
48 |
/* Uncomment and edit this section to provide the service via HTTPS:
|
|
|
49 |
ssl: {
|
|
|
50 |
key: fs.readFileSync('/Applications/XAMPP/etc/ssl.key/server.key'),
|
|
|
51 |
cert: fs.readFileSync('/Applications/XAMPP/etc/ssl.crt/server.crt')
|
|
|
52 |
},
|
|
|
53 |
*/
|
|
|
54 |
nodeStatic: {
|
|
|
55 |
cache: 3600 // seconds to cache served files
|
|
|
56 |
}
|
|
|
57 |
},
|
|
|
58 |
utf8encode = function (str) {
|
|
|
59 |
return unescape(encodeURIComponent(str));
|
|
|
60 |
},
|
|
|
61 |
fileServer = new nodeStatic.Server(options.publicDir, options.nodeStatic),
|
|
|
62 |
nameCountRegexp = /(?:(?: \(([\d]+)\))?(\.[^.]+))?$/,
|
|
|
63 |
nameCountFunc = function (s, index, ext) {
|
|
|
64 |
return ' (' + ((parseInt(index, 10) || 0) + 1) + ')' + (ext || '');
|
|
|
65 |
},
|
|
|
66 |
FileInfo = function (file) {
|
|
|
67 |
this.name = file.name;
|
|
|
68 |
this.size = file.size;
|
|
|
69 |
this.type = file.type;
|
|
|
70 |
this.delete_type = 'DELETE';
|
|
|
71 |
},
|
|
|
72 |
UploadHandler = function (req, res, callback) {
|
|
|
73 |
this.req = req;
|
|
|
74 |
this.res = res;
|
|
|
75 |
this.callback = callback;
|
|
|
76 |
},
|
|
|
77 |
serve = function (req, res) {
|
|
|
78 |
res.setHeader(
|
|
|
79 |
'Access-Control-Allow-Origin',
|
|
|
80 |
options.accessControl.allowOrigin
|
|
|
81 |
);
|
|
|
82 |
res.setHeader(
|
|
|
83 |
'Access-Control-Allow-Methods',
|
|
|
84 |
options.accessControl.allowMethods
|
|
|
85 |
);
|
|
|
86 |
var handleResult = function (result, redirect) {
|
|
|
87 |
if (redirect) {
|
|
|
88 |
res.writeHead(302, {
|
|
|
89 |
'Location': redirect.replace(
|
|
|
90 |
/%s/,
|
|
|
91 |
encodeURIComponent(JSON.stringify(result))
|
|
|
92 |
)
|
|
|
93 |
});
|
|
|
94 |
res.end();
|
|
|
95 |
} else {
|
|
|
96 |
res.writeHead(200, {
|
|
|
97 |
'Content-Type': req.headers.accept
|
|
|
98 |
.indexOf('application/json') !== -1 ?
|
|
|
99 |
'application/json' : 'text/plain'
|
|
|
100 |
});
|
|
|
101 |
res.end(JSON.stringify(result));
|
|
|
102 |
}
|
|
|
103 |
},
|
|
|
104 |
setNoCacheHeaders = function () {
|
|
|
105 |
res.setHeader('Pragma', 'no-cache');
|
|
|
106 |
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
|
107 |
res.setHeader('Content-Disposition', 'inline; filename="files.json"');
|
|
|
108 |
},
|
|
|
109 |
handler = new UploadHandler(req, res, handleResult);
|
|
|
110 |
switch (req.method) {
|
|
|
111 |
case 'OPTIONS':
|
|
|
112 |
res.end();
|
|
|
113 |
break;
|
|
|
114 |
case 'HEAD':
|
|
|
115 |
case 'GET':
|
|
|
116 |
if (req.url === '/') {
|
|
|
117 |
setNoCacheHeaders();
|
|
|
118 |
if (req.method === 'GET') {
|
|
|
119 |
handler.get();
|
|
|
120 |
} else {
|
|
|
121 |
res.end();
|
|
|
122 |
}
|
|
|
123 |
} else {
|
|
|
124 |
fileServer.serve(req, res);
|
|
|
125 |
}
|
|
|
126 |
break;
|
|
|
127 |
case 'POST':
|
|
|
128 |
setNoCacheHeaders();
|
|
|
129 |
handler.post();
|
|
|
130 |
break;
|
|
|
131 |
case 'DELETE':
|
|
|
132 |
handler.destroy();
|
|
|
133 |
break;
|
|
|
134 |
default:
|
|
|
135 |
res.statusCode = 405;
|
|
|
136 |
res.end();
|
|
|
137 |
}
|
|
|
138 |
};
|
|
|
139 |
fileServer.respond = function (pathname, status, _headers, files, stat, req, res, finish) {
|
|
|
140 |
if (!options.safeFileTypes.test(files[0])) {
|
|
|
141 |
// Force a download dialog for unsafe file extensions:
|
|
|
142 |
res.setHeader(
|
|
|
143 |
'Content-Disposition',
|
|
|
144 |
'attachment; filename="' + utf8encode(path.basename(files[0])) + '"'
|
|
|
145 |
);
|
|
|
146 |
} else {
|
|
|
147 |
// Prevent Internet Explorer from MIME-sniffing the content-type:
|
|
|
148 |
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
|
149 |
}
|
|
|
150 |
nodeStatic.Server.prototype.respond
|
|
|
151 |
.call(this, pathname, status, _headers, files, stat, req, res, finish);
|
|
|
152 |
};
|
|
|
153 |
FileInfo.prototype.validate = function () {
|
|
|
154 |
if (options.minFileSize && options.minFileSize > this.size) {
|
|
|
155 |
this.error = 'File is too small';
|
|
|
156 |
} else if (options.maxFileSize && options.maxFileSize < this.size) {
|
|
|
157 |
this.error = 'File is too big';
|
|
|
158 |
} else if (!options.acceptFileTypes.test(this.name)) {
|
|
|
159 |
this.error = 'Filetype not allowed';
|
|
|
160 |
}
|
|
|
161 |
return !this.error;
|
|
|
162 |
};
|
|
|
163 |
FileInfo.prototype.safeName = function () {
|
|
|
164 |
// Prevent directory traversal and creating hidden system files:
|
|
|
165 |
this.name = path.basename(this.name).replace(/^\.+/, '');
|
|
|
166 |
// Prevent overwriting existing files:
|
|
|
167 |
while (_existsSync(options.uploadDir + '/' + this.name)) {
|
|
|
168 |
this.name = this.name.replace(nameCountRegexp, nameCountFunc);
|
|
|
169 |
}
|
|
|
170 |
};
|
|
|
171 |
FileInfo.prototype.initUrls = function (req) {
|
|
|
172 |
if (!this.error) {
|
|
|
173 |
var that = this,
|
|
|
174 |
baseUrl = (options.ssl ? 'https:' : 'http:') +
|
|
|
175 |
'//' + req.headers.host + options.uploadUrl;
|
|
|
176 |
this.url = this.delete_url = baseUrl + encodeURIComponent(this.name);
|
|
|
177 |
Object.keys(options.imageVersions).forEach(function (version) {
|
|
|
178 |
if (_existsSync(
|
|
|
179 |
options.uploadDir + '/' + version + '/' + that.name
|
|
|
180 |
)) {
|
|
|
181 |
that[version + '_url'] = baseUrl + version + '/' +
|
|
|
182 |
encodeURIComponent(that.name);
|
|
|
183 |
}
|
|
|
184 |
});
|
|
|
185 |
}
|
|
|
186 |
};
|
|
|
187 |
UploadHandler.prototype.get = function () {
|
|
|
188 |
var handler = this,
|
|
|
189 |
files = [];
|
|
|
190 |
fs.readdir(options.uploadDir, function (err, list) {
|
|
|
191 |
list.forEach(function (name) {
|
|
|
192 |
var stats = fs.statSync(options.uploadDir + '/' + name),
|
|
|
193 |
fileInfo;
|
|
|
194 |
if (stats.isFile()) {
|
|
|
195 |
fileInfo = new FileInfo({
|
|
|
196 |
name: name,
|
|
|
197 |
size: stats.size
|
|
|
198 |
});
|
|
|
199 |
fileInfo.initUrls(handler.req);
|
|
|
200 |
files.push(fileInfo);
|
|
|
201 |
}
|
|
|
202 |
});
|
|
|
203 |
handler.callback({files: files});
|
|
|
204 |
});
|
|
|
205 |
};
|
|
|
206 |
UploadHandler.prototype.post = function () {
|
|
|
207 |
var handler = this,
|
|
|
208 |
form = new formidable.IncomingForm(),
|
|
|
209 |
tmpFiles = [],
|
|
|
210 |
files = [],
|
|
|
211 |
map = {},
|
|
|
212 |
counter = 1,
|
|
|
213 |
redirect,
|
|
|
214 |
finish = function () {
|
|
|
215 |
counter -= 1;
|
|
|
216 |
if (!counter) {
|
|
|
217 |
files.forEach(function (fileInfo) {
|
|
|
218 |
fileInfo.initUrls(handler.req);
|
|
|
219 |
});
|
|
|
220 |
handler.callback({files: files}, redirect);
|
|
|
221 |
}
|
|
|
222 |
};
|
|
|
223 |
form.uploadDir = options.tmpDir;
|
|
|
224 |
form.on('fileBegin', function (name, file) {
|
|
|
225 |
tmpFiles.push(file.path);
|
|
|
226 |
var fileInfo = new FileInfo(file, handler.req, true);
|
|
|
227 |
fileInfo.safeName();
|
|
|
228 |
map[path.basename(file.path)] = fileInfo;
|
|
|
229 |
files.push(fileInfo);
|
|
|
230 |
}).on('field', function (name, value) {
|
|
|
231 |
if (name === 'redirect') {
|
|
|
232 |
redirect = value;
|
|
|
233 |
}
|
|
|
234 |
}).on('file', function (name, file) {
|
|
|
235 |
var fileInfo = map[path.basename(file.path)];
|
|
|
236 |
fileInfo.size = file.size;
|
|
|
237 |
if (!fileInfo.validate()) {
|
|
|
238 |
fs.unlink(file.path);
|
|
|
239 |
return;
|
|
|
240 |
}
|
|
|
241 |
fs.renameSync(file.path, options.uploadDir + '/' + fileInfo.name);
|
|
|
242 |
if (options.imageTypes.test(fileInfo.name)) {
|
|
|
243 |
Object.keys(options.imageVersions).forEach(function (version) {
|
|
|
244 |
counter += 1;
|
|
|
245 |
var opts = options.imageVersions[version];
|
|
|
246 |
imageMagick.resize({
|
|
|
247 |
width: opts.width,
|
|
|
248 |
height: opts.height,
|
|
|
249 |
srcPath: options.uploadDir + '/' + fileInfo.name,
|
|
|
250 |
dstPath: options.uploadDir + '/' + version + '/' +
|
|
|
251 |
fileInfo.name
|
|
|
252 |
}, finish);
|
|
|
253 |
});
|
|
|
254 |
}
|
|
|
255 |
}).on('aborted', function () {
|
|
|
256 |
tmpFiles.forEach(function (file) {
|
|
|
257 |
fs.unlink(file);
|
|
|
258 |
});
|
|
|
259 |
}).on('error', function (e) {
|
|
|
260 |
console.log(e);
|
|
|
261 |
}).on('progress', function (bytesReceived, bytesExpected) {
|
|
|
262 |
if (bytesReceived > options.maxPostSize) {
|
|
|
263 |
handler.req.connection.destroy();
|
|
|
264 |
}
|
|
|
265 |
}).on('end', finish).parse(handler.req);
|
|
|
266 |
};
|
|
|
267 |
UploadHandler.prototype.destroy = function () {
|
|
|
268 |
var handler = this,
|
|
|
269 |
fileName;
|
|
|
270 |
if (handler.req.url.slice(0, options.uploadUrl.length) === options.uploadUrl) {
|
|
|
271 |
fileName = path.basename(decodeURIComponent(handler.req.url));
|
|
|
272 |
fs.unlink(options.uploadDir + '/' + fileName, function (ex) {
|
|
|
273 |
Object.keys(options.imageVersions).forEach(function (version) {
|
|
|
274 |
fs.unlink(options.uploadDir + '/' + version + '/' + fileName);
|
|
|
275 |
});
|
|
|
276 |
handler.callback({success: !ex});
|
|
|
277 |
});
|
|
|
278 |
} else {
|
|
|
279 |
handler.callback({success: false});
|
|
|
280 |
}
|
|
|
281 |
};
|
|
|
282 |
if (options.ssl) {
|
|
|
283 |
require('https').createServer(options.ssl, serve).listen(port);
|
|
|
284 |
} else {
|
|
|
285 |
require('http').createServer(serve).listen(port);
|
|
|
286 |
}
|
|
|
287 |
}(8888));
|