Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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));