Nevtep Nevtep - 6 months ago 33
Node.js Question

Uploading files using FineUploader in Node.js hangs up server

I've been troubleshooting this bug for over two weeks now, and can't find what's going wrong. I've go a page using FineUploader to upload multiple files. I have a collection of items and each needs to get it's own images. so I'm pasing a parameter with the request to identify to which item the images correspond. I also have an uploader for a picture gallery and for a logo.

I trigger the uploads manually and get the request on the server, then set req.on(data, function() { ... }) to upload the files. This NEVER get's triggered.

This is my code:

$(document).ready(function () {
window.onSubmit = function (uploaderName) {
return function (id, fileName) {
console.log("Setting parameters...")
window[uploaderName].setParams({
farm: $('input[name=farm-name]').val()
});
}
};

window.logoUploader = new qq.FineUploader({
element: $('#logo')[0],
request: {
endpoint: '/upload/farm/logo'
},
autoUpload: false,
debug: true,
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
sizeLimit: 1024000 // 50 kB = 50 * 1024 bytes
},
text: {
uploadButton: '<i class="icon-plus icon-white"></i> Select Logo'
},
callbacks: {
onSubmit: onSubmit("logoUploader")
}
});

window.shareLogoUploader = [];
window.shareLogoUploader[0] = new qq.FineUploader({
element: $('#share-logo-0')[0],
request: {
endpoint: '/upload/farm/share/gallery'
},
autoUpload: false,
debug: true,
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
sizeLimit: 1024000 // 50 kB = 50 * 1024 bytes
},
text: {
uploadButton: '<i class="icon-plus icon-white"></i> Select Logo'
}
});

window.galleryUploader = new qq.FineUploader({
element: $('#gallery')[0],
request: {
endpoint: '/upload/farm/gallery/photo'
},
autoUpload: false,
debug: true,
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
sizeLimit: 2024000 // around 2mbs
},
text: {
uploadButton: '<i class="icon-plus icon-white"></i> Pics'
},
callbacks: {
onComplete: function (event, id, fileName, responseJSON) {
if (responseJSON.success) {
console.log(responseJSON, fileName);
$('#thumbnails').append('<img src="/uploads/' + fileName + '" width="256px"><input type="hidden" name="photos[]" value="' + fileName + '">');
}
},
onSubmit: onSubmit("galleryUploader")
}
});
});

// Shares
var shares = [];
var sharesGallery = [];

$(function () {
$('section#shares button#add-share').click(function (e) {
e.preventDefault();

var title = $('section#shares .control-group input[name=title]');
var size = $('section#shares .control-group select[name=size]');
var type = $('section#shares .control-group select[name=type]');
var price = $('section#shares .control-group input[name=price]');
var amount = $('section#shares .control-group input[name=amount]');
var unit = $('section#shares .control-group select[name=unit]');
var discount_3 = $('input[name=discount-3]').val();
var discount_6 = $('input[name=discount-6]').val();
var discount_12 = $('input[name=discount-12]').val();
var errors = $('section#shares .control-group#errors');
var delivery_period = $('section#shares .control-group input[name=delivery_period]');
var delivery_period_es = $('section#shares .control-group input[name=delivery_period_es]');
var billing_period = $('section#shares .control-group input[name=billing_period]');
var billing_period_es = $('section#shares .control-group input[name=billing_period_es]');
var logoUploader = window.shareLogoUploader[shares.length];
var logoButtonId = "share-logo-" + shares.length;

shares.push({
title: title.val(),
currency: $('select[name=currency]').children(':selected').val(),
size: size.children(':selected').val(),
type: type.children(':selected').val(),
price: price.val() * 100.0,
unit: unit.children(':selected').val(),
amount: amount.val(),
discounts: {
'3': [discount_3, price.val() * discount_3 / 100.0],
'6': [discount_6, price.val() * discount_6 / 100.0],
'12': [discount_12, price.val() * discount_12 / 100.0]
},
delivery_period: delivery_period.val(),
delivery_period_es: delivery_period_es.val(),
billing_period: billing_period.val(),
billing_period_es: billing_period_es.val()
});

var newLogoButtonId = "share-logo-" + shares.length;

sharesGallery.push({
name: type.children(':selected').val() + "-" + size.children(':selected').val(),
logoUploader: logoUploader
})
$("#" + logoButtonId).css('display', 'none');
$("#" + logoButtonId).parent().append('<div id="' + newLogoButtonId + '"></div>');

window.shareLogoUploader[shares.length] = new qq.FineUploader({
element: $("#" + newLogoButtonId)[0],
request: {
endpoint: '/upload/farm/share/gallery'
},
autoUpload: false,
debug: true,
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
sizeLimit: 1024000 // 50 kB = 50 * 1024 bytes
},
text: {
uploadButton: '<i class="icon-plus icon-white"></i> Select Logo'
}
});

console.log(shares);

return;
}
});


This triggers the uploads correctly, I get the request on the server and handle it this way, then the server hangs on waiting for data that never comes, re req.on Data naver gets triggered. This is the Server side code:

var fs = require('fs'), util = require('util'), uuid = require('node-uuid');

var uploadpath = __dirname + "/../public/uploads/";

var slugify = exports.slugify = function(value) {
return value.toLowerCase().replace(/-+/g, '').replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
};
// Route that takes the post upload request and sends the server response
exports.upload = function(req, res) {
winston.info("Upload parameters", [req.route.params[0], req.query.farm]);
uploadpath = __dirname + "/../public/uploads/";
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
switch(req.route.params[0]) {
case 'farm/logo':
uploadpath += "../farms/" + slugify(req.query.farm);
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
uploadpath += "/" + "logo." + req.header('x-file-name').split(".").pop();
break;

case 'farm/gallery/photo':
uploadpath += "../farms/" + slugify(req.query.farm);
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
uploadpath += "/gallery";
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
var fname = req.header('x-file-name');
uploadpath += "/" + fname;
break;

case 'farm/share/gallery':
uploadpath += "../farms/" + slugify(req.query.farm);
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
uploadpath += "/shares";
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
uploadpath += "/" + slugify(req.query.share);
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
uploadpath += "/gallery";
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
var fname = req.header('x-file-name');
uploadpath += "/" + fname;
break;
case 'courier/license':
case 'courier/registration':
uploadpath += "../couriers/" + slugify(req.query.courier);
if(!fs.existsSync(uploadpath)) {
fs.mkdirSync(uploadpath);
}
var fname = req.header('x-file-name');
uploadpath += "/" + fname;
break;

default:
var fname = req.header('x-file-name');
uploadpath += fname;
break;
}

winston.info("Uploading to ", uploadpath);

uploadFile(req, uploadpath, function(data) {
if(data.success)
res.send(JSON.stringify(data), {
'Content-Type' : 'text/plain'
}, 200);
else
res.send(JSON.stringify(data), {
'Content-Type' : 'text/plain'
}, 404);
});
}
// Mainfunction to recieve and process the file upload data asynchronously
var uploadFile = function(req, targetfile, callback) {

// Moves the uploaded file from temp directory to it's destination
// and calls the callback with the JSON-data that could be returned.
var moveToDestination = function(sourcefile, targetfile) {
moveFile(sourcefile, targetfile, function(err) {
if(!err)
callback({
success : true
});
else
callback({
success : false,
error : err
});
});
};
// Direct async xhr stream data upload, yeah baby.
if(req.xhr) {

// Be sure you can write to '/tmp/'
var tmpfile = '/tmp/' + uuid.v1();

// Open a temporary writestream
var ws = fs.createWriteStream(tmpfile);
ws.on('error', function(err) {
console.log("uploadFile() - req.xhr - could not open writestream.");
callback({
success : false,
error : "Sorry, could not open writestream."
});
});
ws.on('close', function(err) {
moveToDestination(tmpfile, targetfile);
});
// Writing filedata into writestream
req.on('data', function(data) {
ws.write(data);
});
req.on('end', function() {
ws.end();
});
}

// Old form-based upload
else {
moveToDestination(req.files.qqfile.path, targetfile);
}
};
// Moves a file asynchronously over partition borders
var moveFile = function(source, dest, callback) {
var is = fs.createReadStream(source)

is.on('error', function(err) {
console.log('moveFile() - Could not open readstream.', err);
callback('Sorry, could not open readstream.')
});
is.on('end', function() {
fs.unlinkSync(source);
callback();
});
var os = fs.createWriteStream(dest);
os.on('error', function(err) {
console.log('moveFile() - Could not open writestream.', err);
callback('Sorry, could not open writestream.');
});

is.pipe(os);
}


Can someone point me in the right direction to get file uploading with diferent parameters for each bunch of files?

Thank you in advance.

Answer

It looks like you are using the Node example, or perhaps a modified version of the Node example from the server directory of the Fine Uploader Github project. That example has not been updated to handle multipart encoded requests, which Fine Uploader now sends by default for all browsers.

I don't have much experience with Node, but if you are using Express, it seems like you can/should just use req.files to grab the files associated with the request.

I'll open up a case in the Widen/fine-uploader-server repo to remind me to update the node example.

Comments