Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
#!/usr/bin/env node
2
/*
3
 * jQuery File Upload Plugin Node.js Example 2.1.1
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
/* jshint nomen:false */
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
            inlineFileTypes: /\.(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
                allowHeaders: 'Content-Type, Content-Range, Content-Disposition'
48
            },
49
            /* Uncomment and edit this section to provide the service via HTTPS:
50
            ssl: {
51
                key: fs.readFileSync('/Applications/XAMPP/etc/ssl.key/server.key'),
52
                cert: fs.readFileSync('/Applications/XAMPP/etc/ssl.crt/server.crt')
53
            },
54
            */
55
            nodeStatic: {
56
                cache: 3600 // seconds to cache served files
57
            }
58
        },
59
        utf8encode = function (str) {
60
            return unescape(encodeURIComponent(str));
61
        },
62
        fileServer = new nodeStatic.Server(options.publicDir, options.nodeStatic),
63
        nameCountRegexp = /(?:(?: \(([\d]+)\))?(\.[^.]+))?$/,
64
        nameCountFunc = function (s, index, ext) {
65
            return ' (' + ((parseInt(index, 10) || 0) + 1) + ')' + (ext || '');
66
        },
67
        FileInfo = function (file) {
68
            this.name = file.name;
69
            this.size = file.size;
70
            this.type = file.type;
71
            this.deleteType = 'DELETE';
72
        },
73
        UploadHandler = function (req, res, callback) {
74
            this.req = req;
75
            this.res = res;
76
            this.callback = callback;
77
        },
78
        serve = function (req, res) {
79
            res.setHeader(
80
                'Access-Control-Allow-Origin',
81
                options.accessControl.allowOrigin
82
            );
83
            res.setHeader(
84
                'Access-Control-Allow-Methods',
85
                options.accessControl.allowMethods
86
            );
87
            res.setHeader(
88
                'Access-Control-Allow-Headers',
89
                options.accessControl.allowHeaders
90
            );
91
            var handleResult = function (result, redirect) {
92
                    if (redirect) {
93
                        res.writeHead(302, {
94
                            'Location': redirect.replace(
95
                                /%s/,
96
                                encodeURIComponent(JSON.stringify(result))
97
                            )
98
                        });
99
                        res.end();
100
                    } else {
101
                        res.writeHead(200, {
102
                            'Content-Type': req.headers.accept
103
                                .indexOf('application/json') !== -1 ?
104
                                        'application/json' : 'text/plain'
105
                        });
106
                        res.end(JSON.stringify(result));
107
                    }
108
                },
109
                setNoCacheHeaders = function () {
110
                    res.setHeader('Pragma', 'no-cache');
111
                    res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
112
                    res.setHeader('Content-Disposition', 'inline; filename="files.json"');
113
                },
114
                handler = new UploadHandler(req, res, handleResult);
115
            switch (req.method) {
116
            case 'OPTIONS':
117
                res.end();
118
                break;
119
            case 'HEAD':
120
            case 'GET':
121
                if (req.url === '/') {
122
                    setNoCacheHeaders();
123
                    if (req.method === 'GET') {
124
                        handler.get();
125
                    } else {
126
                        res.end();
127
                    }
128
                } else {
129
                    fileServer.serve(req, res);
130
                }
131
                break;
132
            case 'POST':
133
                setNoCacheHeaders();
134
                handler.post();
135
                break;
136
            case 'DELETE':
137
                handler.destroy();
138
                break;
139
            default:
140
                res.statusCode = 405;
141
                res.end();
142
            }
143
        };
144
    fileServer.respond = function (pathname, status, _headers, files, stat, req, res, finish) {
145
        // Prevent browsers from MIME-sniffing the content-type:
146
        _headers['X-Content-Type-Options'] = 'nosniff';
147
        if (!options.inlineFileTypes.test(files[0])) {
148
            // Force a download dialog for unsafe file extensions:
149
            _headers['Content-Type'] = 'application/octet-stream';
150
            _headers['Content-Disposition'] = 'attachment; filename="' +
151
                utf8encode(path.basename(files[0])) + '"';
152
        }
153
        nodeStatic.Server.prototype.respond
154
            .call(this, pathname, status, _headers, files, stat, req, res, finish);
155
    };
156
    FileInfo.prototype.validate = function () {
157
        if (options.minFileSize && options.minFileSize > this.size) {
158
            this.error = 'File is too small';
159
        } else if (options.maxFileSize && options.maxFileSize < this.size) {
160
            this.error = 'File is too big';
161
        } else if (!options.acceptFileTypes.test(this.name)) {
162
            this.error = 'Filetype not allowed';
163
        }
164
        return !this.error;
165
    };
166
    FileInfo.prototype.safeName = function () {
167
        // Prevent directory traversal and creating hidden system files:
168
        this.name = path.basename(this.name).replace(/^\.+/, '');
169
        // Prevent overwriting existing files:
170
        while (_existsSync(options.uploadDir + '/' + this.name)) {
171
            this.name = this.name.replace(nameCountRegexp, nameCountFunc);
172
        }
173
    };
174
    FileInfo.prototype.initUrls = function (req) {
175
        if (!this.error) {
176
            var that = this,
177
                baseUrl = (options.ssl ? 'https:' : 'http:') +
178
                    '//' + req.headers.host + options.uploadUrl;
179
            this.url = this.deleteUrl = baseUrl + encodeURIComponent(this.name);
180
            Object.keys(options.imageVersions).forEach(function (version) {
181
                if (_existsSync(
182
                        options.uploadDir + '/' + version + '/' + that.name
183
                    )) {
184
                    that[version + 'Url'] = baseUrl + version + '/' +
185
                        encodeURIComponent(that.name);
186
                }
187
            });
188
        }
189
    };
190
    UploadHandler.prototype.get = function () {
191
        var handler = this,
192
            files = [];
193
        fs.readdir(options.uploadDir, function (err, list) {
194
            list.forEach(function (name) {
195
                var stats = fs.statSync(options.uploadDir + '/' + name),
196
                    fileInfo;
197
                if (stats.isFile() && name[0] !== '.') {
198
                    fileInfo = new FileInfo({
199
                        name: name,
200
                        size: stats.size
201
                    });
202
                    fileInfo.initUrls(handler.req);
203
                    files.push(fileInfo);
204
                }
205
            });
206
            handler.callback({files: files});
207
        });
208
    };
209
    UploadHandler.prototype.post = function () {
210
        var handler = this,
211
            form = new formidable.IncomingForm(),
212
            tmpFiles = [],
213
            files = [],
214
            map = {},
215
            counter = 1,
216
            redirect,
217
            finish = function () {
218
                counter -= 1;
219
                if (!counter) {
220
                    files.forEach(function (fileInfo) {
221
                        fileInfo.initUrls(handler.req);
222
                    });
223
                    handler.callback({files: files}, redirect);
224
                }
225
            };
226
        form.uploadDir = options.tmpDir;
227
        form.on('fileBegin', function (name, file) {
228
            tmpFiles.push(file.path);
229
            var fileInfo = new FileInfo(file, handler.req, true);
230
            fileInfo.safeName();
231
            map[path.basename(file.path)] = fileInfo;
232
            files.push(fileInfo);
233
        }).on('field', function (name, value) {
234
            if (name === 'redirect') {
235
                redirect = value;
236
            }
237
        }).on('file', function (name, file) {
238
            var fileInfo = map[path.basename(file.path)];
239
            fileInfo.size = file.size;
240
            if (!fileInfo.validate()) {
241
                fs.unlink(file.path);
242
                return;
243
            }
244
            fs.renameSync(file.path, options.uploadDir + '/' + fileInfo.name);
245
            if (options.imageTypes.test(fileInfo.name)) {
246
                Object.keys(options.imageVersions).forEach(function (version) {
247
                    counter += 1;
248
                    var opts = options.imageVersions[version];
249
                    imageMagick.resize({
250
                        width: opts.width,
251
                        height: opts.height,
252
                        srcPath: options.uploadDir + '/' + fileInfo.name,
253
                        dstPath: options.uploadDir + '/' + version + '/' +
254
                            fileInfo.name
255
                    }, finish);
256
                });
257
            }
258
        }).on('aborted', function () {
259
            tmpFiles.forEach(function (file) {
260
                fs.unlink(file);
261
            });
262
        }).on('error', function (e) {
263
            console.log(e);
264
        }).on('progress', function (bytesReceived) {
265
            if (bytesReceived > options.maxPostSize) {
266
                handler.req.connection.destroy();
267
            }
268
        }).on('end', finish).parse(handler.req);
269
    };
270
    UploadHandler.prototype.destroy = function () {
271
        var handler = this,
272
            fileName;
273
        if (handler.req.url.slice(0, options.uploadUrl.length) === options.uploadUrl) {
274
            fileName = path.basename(decodeURIComponent(handler.req.url));
275
            if (fileName[0] !== '.') {
276
                fs.unlink(options.uploadDir + '/' + fileName, function (ex) {
277
                    Object.keys(options.imageVersions).forEach(function (version) {
278
                        fs.unlink(options.uploadDir + '/' + version + '/' + fileName);
279
                    });
280
                    handler.callback({success: !ex});
281
                });
282
                return;
283
            }
284
        }
285
        handler.callback({success: false});
286
    };
287
    if (options.ssl) {
288
        require('https').createServer(options.ssl, serve).listen(port);
289
    } else {
290
        require('http').createServer(serve).listen(port);
291
    }
292
}(8888));