I have been working on my node.js MVC CMS recently, and the last module missing to have all the core features was a file management system.
I was able to put together two nice plugins, clean it up little bit, and integrate in my JavaScript MVC paradigm.
For the node.js server side, I used formidable lib. For some reason, the part that was the biggest trouble was actually saving the file. Here is what the final solution looked like:
var formidable = require('formidable'),
c = require('../../config'),
fs = require('fs');
exports.upload = function(req, res){
var form = new formidable.IncomingForm();
// Parse file.
form.parse(req, function(err, fields, files) {
if(files.fileToUpload){
// Read file.
fs.readFile(files.fileToUpload.path, function(err, data){
// Save file.
fs.writeFile(c.config.appPath + '/content/files/' +
files.fileToUpload.name,
data,
'utf8',
function (err) {
if (err){
// throw err;
res.writeHead(200, {'content-type': 'text/plain'});
res.write(JSON.stringify({
isSucessful: false,
message: 'Something went wrong!'
}));
res.end();
} else {
// Sucess.
res.writeHead(200, {'content-type': 'text/plain'});
res.write(JSON.stringify({
isSucessful: true,
message: 'File was saved!'
}));
res.end();
}
});
});
} else {
res.writeHead(200, {'content-type': 'text/plain'});
res.write(JSON.stringify({
isSucessful: false,
message: 'Did not receive any file!'
}));
res.end();
}
});
};
For the client, I found this tutorial about how to do an HTML5 AJAX uploader. After I cleaned up the code little bit, this is what it looks like:
var messageWrp = $('#fileDataMsg', content);
// Source:
// http://www.matlus.com/html5-file-upload-with-progress/
$('#fileToUpload', content).change(function(){
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024){
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
} else {
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
}
// File info message.
messageWrp.html('Name: ' + file.name + ' Size: ' + fileSize + ' Type: ' + file.type);
}
});
$('#uploadFileBtn', content).click(function () {
// Form data.
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
var xhr = new XMLHttpRequest();
// Progress listerner.
xhr.upload.addEventListener("progress", function (evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
messageWrp.html(percentComplete.toString() + '%');
}
else {
messageWrp.html('unable to compute');
}
}, false);
// On finished.
xhr.addEventListener("load", function (evt) {
// Parse json.
var obj = $.parseJSON(evt.target.responseText);
window.location.hash = 'File';
// Show success message.
MVC.message.show({text: obj.message, hideDealy: 2000});
}, false);
// On failed.
xhr.addEventListener("error", function (evt) {
MVC.message.show({text: 'There was an error attempting to upload the file.',
hideDealy: 2000});
}, false);
// On cancel.
xhr.addEventListener("abort", function (evt) {
MVC.message.show({text: 'The upload has been canceled by the user or the browser dropped the connection.',
hideDealy: 2000});
}, false);
xhr.open("POST", "/upload");
xhr.send(fd);
return false;
});
Finally, this is the HTML part:
<form enctype="multipart/form-data" method="post" action="upload/"> <p> <label for="fileToUpload">Select a File to Upload</label> <input type="file" name="fileToUpload" id="fileToUpload" /> </p> <p> <a class="button" href="#" id="uploadFileBtn">Upload</a> </p> <p id="fileDataMsg"></p> </form>