Skip to content

Instantly share code, notes, and snippets.

@asnowfix
Created October 17, 2012 19:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asnowfix/3907749 to your computer and use it in GitHub Desktop.
Save asnowfix/3907749 to your computer and use it in GitHub Desktop.
test case showing difference between node-formidable multipart/form-data streams and fs.File streams
var fs = require("fs");
var path = require("path");
var express = require("express");
var util = require("util");
var querystring = require("querystring");
var temp = require("temp");
var zipstream = require('zipstream');
var formidable = require('formidable');
function Zipper(config, next) {
function HttpError(msg, statusCode) {
Error.captureStackTrace(this, this);
this.statusCode = statusCode || 500; // Internal-Server-Error
this.message = msg || 'Error';
}
util.inherits(HttpError, Error);
HttpError.prototype.name = "HTTP Error";
// (simple) parameters checking
config.pathname = config.pathname || '/zip';
var app = express.createServer();
app.use(express.logger('dev'));
// Error handler
app.error(function(err, req, res, next){
res.status(err.statusCode || 500).send(err.toString());
});
// Create archive
app.post('/zip', function(req, res, next) {
var form, zip, tasks, zipping = false;
var files = [], fields = [];
try {
if (!req.is('multipart/form-data')) {
next(new HttpError("Not a multipart request", 415 /*Unsupported Media Type*/));
return;
}
tasks = [];
res.contentType('application/zip');
zip = zipstream.createZip({level: 1});
zip.pipe(res);
form = new formidable.IncomingForm();
// We use an intermediate temporary directory,
// because the following issue prevents piping
// directly node-formidable input-streams into
// a node-zipstream output-stream (archive).
// <https://github.com/felixge/node-formidable/issues/182>,
if (config.useFiles) {
console.log("using file-based zipping");
form.uploadDir = temp.path({prefix: 'zipper.'}) + '.d';
fs.mkdirSync(form.uploadDir);
form.on('file', function(field, file) {
files.push(file);
tasks.push(function() {
zipping = true;
zip.addFile(new fs.createReadStream(file.path), {name: file.name}, function() {
console.log("added", file.name);
zipping = false;
_nextTask();
});
});
});
form.on('field', function(field, value) {
console.log(field, value);
fields.push([field, value]);
});
} else {
console.log("using part-based zipping");
form.onPart = function(part) {
if (!part.filename) {
// let formidable handle all non-file parts
form.handlePart(part);
} else {
tasks.push(function() {
zipping = true;
zip.addFile(part, {name: part.filename}, function() {
console.log("added", part.filename);
zipping = false;
_nextTask();
});
});
_nextTask();
}
};
}
form.on('end', function() {
tasks.push(function() {
zip.finalize(function(written) {
res.status(201 /*Created*/).end();
if (config.useFiles) {
_clean();
}
});
});
_nextTask();
});
function _nextTask() {
if (!zipping) {
var task = tasks.shift();
task && task();
}
}
form.parse(req, function(err) {
if (err) {
next(err);
}
});
function _clean(next) {
var file, count = files.length;
function _rmdir() {
(count === 0) && fs.rmdir(form.uploadDir, next);
}
_rmdir();
while ((file = files.shift())) {
fs.unlink(file.path, function(err) {
if (err) {
next(err);
return;
}
count--;
_rmdir();
});
}
}
} catch(e) {
console.error(e.stack);
next(new HttpError(e.msg));
}
});
app.listen(9019, "127.0.0.1", null /*backlog*/, function() {
return next();
});
this.quit = function() {
app.close();
};
}
if (path.basename(process.argv[1]) === "formidable-issue182.js") {
var zipper = new Zipper({
useFiles: !!process.argv[2]
}, function(err){
if (err) {
process.exit(err);
}
if (process.send) {
process.send({ok: true});
}
});
} else {
module.exports = Zipper;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment