How to handle plupload in expressjs using multer with chunking? - node.js

I am implementing a file upload using Plupload in the frontend and express nodejs in the backend with multer middleware for multipart/form upload. There is currently no example available, so this is what I got so far:
HTML frontend:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Test</title>
</head>
<ul id="filelist"></ul>
<br />
<div id="container">
<a id="browse" href="javascript:;">[Browse...]</a>
<a id="start-upload" href="javascript:;">[Start Upload]</a>
<br />
<pre id="console"></pre>
</div>
<script src="/plupload/js/plupload.full.min.js"></script>
<script type="text/javascript">
var uploader = new plupload.Uploader({
browse_button: 'browse', // this can be an id of a DOM element or the DOM element itself
url: '/upload'
});
uploader.init();
uploader.bind('FilesAdded', function(up, files) {
var html = '';
plupload.each(files, function(file) {
html += '<li id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b></li>';
});
document.getElementById('filelist').innerHTML += html;
});
uploader.bind('UploadProgress', function(up, file) {
document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '<span>' + file.percent + "%</span>";
});
uploader.bind('Error', function(up, err) {
document.getElementById('console').innerHTML += "\nError #" + err.code + ": " + err.message;
});
document.getElementById('start-upload').onclick = function() {
uploader.start();
};
</script>
</html>
It basically just the plupload quickstart guide: http://www.plupload.com/docs/v2/Getting-Started
Backend using node express. I trimmed my code down to a minimum working version for use here on SO:
const path = require('path');
const express = require('express');
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
// Create express
const app = express();
app.use(express.static('public'));
app.post('/upload', upload.array('file'), function(req, res){
console.log(req.files);
})
app.listen(3000, function () {
console.log('App running...');
});
Basically, just a regular express app with multer package and serving static files.
Question:
How do I upload files using Plupload in the front-end and NodeJS (using express, multer) in the backend? It should also support chunking.

You can use the fileFilter function to validate your files before they are getting uploaded, this function enables you to validate filenames, extensions and which files should be uploaded and which should be skipped.
For, eg, Let's assume that you want the user to upload only "PDF" files, you can write a filter like this,
multer({
fileFilter: function (req, file, cb) {
if (file.mimetype !== 'application/pdf') {
req.fileValidationError = 'Only PDF files can be uploaded';
return cb(null, false, new Error('Only PDF files can be uploaded'));
}
cb(null, true);
}
});
Just in case if you want to restrict the user to upload files within certain MB, you can make use of the limits property which can be set as,
limits: { fileSize: the_file_size_which_you_want_to_allow }
And finally if you want to have a common file naming pattern in your destination directory (where the files gets uploaded) you can make use of the fileName function like this, (in the below example we are appending a hyphen and timestamp to the filename).
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
UPDATE
You can make use of the plupload node module which will take care of the express-plupload communication which you're trying to sort out.
Hope this helps!

Related

How do I display a user-uploaded image with Express and EJS?

Basically what the title says. Here's what I have got:
I read the images from a form using multer, which I set up like this:
var multer = require('multer');
var express = require('express');
var path = require('path');
var router = express.Router();
var storage = multer.diskStorage({
destination: function(req, file, callback){
callback(null, './imgs/')
},
filename: function(req, file, callback) {
callback(null, "tmp" + path.extname(file.originalname));
}
});
router.route('/items/item-create/:cat_id')
.post(upload.single('img'), itemController.postItem);
This works as intended, so I'll not provide the code for itemController.postItem.
I saved the images in a folder called "imgs", which I have made static, like so:
var app = express();
app.use(express.static(path.join(__dirname, 'imgs')));
The images each correspond to a mongoDB document(called "item"), so they are named as "item._id.jpg". I wrote a basic ejs template("item.ejs") which accepts the item document and uses its _id to generate the filename and use it in an "img" tag:
<!DOCTYPE html>
<html>
<head>
<title>Testing</title>
</head>
<body>
<p><%=it.name%></p>
<img src="<%= it._id.toString() + '.jpg' %>" alt="image">
</body>
</html>
This is the function which renders the template, invoked on a GET request to /items/:item_id :
exports.getItem = function(req, res){
Item.findOne({ _id:req.params.item_id }, function(err, item){
if(err) res.send(err);
console.log("In getItem...");
console.log(req.params.item_id);
console.log(item);
res.render('item', { it: item });
});
};
It shows the item name correctly, but it refuses to load the image, and somehow the program sends another GET request to the same url, but with the image name as the :item_id parameter, which obviously throws an error about being unable to read property "name" of "undefined". What am I doing wrong here?
I tried this, but it threw an 'ERR_HTTP_HEADERS_SENT'.
I solved it, turns out I had mounted the static folder incorrectly. As I needed the images at /items/ (and also at /categories/:cat_id/cat-details, which I did not mention in my question), I mounted it as follows:
app.use(['/items/', '/categories/:cat_id/details/'], express.static(path.join(__dirname, 'imgs')));

Stylesheet not loaded, even though statics has been configured

I'm going crazy over this...
I have this simple NodeJS server running - it serves the ./start.html file fine, but the CSS-file is not loaded.
My folder structure looks like this:
/public/css/styles.css
/interface.js (the Node-file)
/start.html
Node is running this code:
const app = express();
const hostname = '127.0.0.1';
const port = 3000;
let serials;
app.use(express.static('public'));
// Make these folders accessible by our HTML-files
app.use('/css', express.static(__dirname + '/public/css'));
//app.use('/js', express.static(__dirname + '/public/js')); // Not used at the moment
//app.use('/images', express.static(__dirname + '/public/images')); // Not used at the moment
app.get('/start', (req, res) => {
const fs = require('fs');
var content;
// We load the file asynchronously and pass on the content to the screen when loaded.
fs.readFile('./start.html', function read(err, data) {
if (err) {
throw err;
}
res.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': data.length, 'Expires': new Date().toUTCString()});
res.end(data);
});
});
The start.html file looks like this:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>start</title>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<link href="css/styles.css" rel="stylesheet" type="/text/css">
<script type="text/javascript">
$(document).ready(function() {
etc...
When accessed, using localhost:3000/start, it only shows the HTML-code.
When opening localhost:3000/css/styles.css it displays the stylesheet just fine.
The browser console also does not show any CSS-file loaded.
Any suggestions, please?
Simple mistake: the CSS linkage had a "/text/css" instead of "text/css" and not an error in the JS after all. Now it works perfectly.

nodejs multer file upload, what happens file names are the same from 2 places

The concept is still not clear to me yet.
For a very basic case, i can upload a file (say myFile.txt) using multer, and keep its original name in the server side.
const upload = multer({ dest: `${UPLOAD_PATH}/` }); // multer configuration
Now another person happen to upload a different file with same file name myFile.txt to the same folder in server side. Will it overwrite the previous one ?
How do you normally manage this ? Thanks !
Will it overwrite the previous one ?
Yes it will definitely replace with the new one.Here is my code .In this code if you use a same file name from different locations or from the same location it won't replaces.It keeps both the files in destination.Server code server.js
var express=require('express');
var multer=require('multer');
var path = require('path')
var app=express();
var ejs = require('ejs')
app.set('view engine', 'ejs')
var storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, './public/uploads')
},
filename: function(req, file, callback) {
callback(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
//callback(null, file.originalname)
}
})
app.get('/api/file',function(req,res){
res.render('index');
});
app.post('/api/file', function(req, res) {
var upload = multer({
storage: storage}).single('userFile');
upload(req, res, function(err) {
console.log("File uploaded");
res.end('File is uploaded')
})
})
app.listen(3000,function(){
console.log("working on port 3000");
});
If you observe the code callback(null, file.originalname) this line will keep the original file name in destination and will replace the file when it gets the same name.If you don't want this then use callback(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname)).This callback changes the name of file in destination.
Make a views folder and keep this file in it.ejs code from which you can choose a file to upload index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form id="uploadForm" enctype="multipart/form-data" method="post">
<input type="file" name="userFile" />
<input type="submit" value="Upload File" name="submit">
</form>
</body>
</html>
Run the code as node server.js.Open the browser and type http://localhost:3000/api/file.Choose a file to upload and see the destination folder.Hope this helps for you.
actually Multer saves the file in a given path (as specied by you) but with a different randomised filename. It will not override the file even if the name of the file is the same.
So your UPLOAD_PATH will have multiple files even if you are loading the the file with same name again.
You can go a step further to ensure the file names are more specific:
filename: (req, file, cb) => {
let fn = file.originalname.split(path.extname(file.originalname))[0] + '-' + Date.now() + path.extname(file.originalname);
cb(null, /* file.originalname */ fn);
}

Express static css not served

I have been trying to figure this out for hours and my head is about to explode. I really hope it's not some stupid little detail I missed...
I have a server-side rendering react application set-up. Everything is going fine. The only problem I have is that I can't seem to get the css to load.
Here is my file tree (without node_modules): https://i.stack.imgur.com/xevUb.png'
I have the following code in my server.js file
app.use('static', express.static(__dirname + '/public'));
app.use('dist', express.static(__dirname + '/dist'));
app.get('*', (req, res) => {
match({
routes,
location: req.url
}, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message)
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
var html = renderToString( < RouterContext { ...renderProps
}
/>);
res.status(200).send(template({
body: html
}));
}
else {
res.status(400).send('Not found.')
}
});
});
And this is my template.js file :
export default ({ body }) => {
return `
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
</head>
<body>
<div id="root">${body}</div>
</body>
<script src="dist/client.js"></script>
</html>
`;
};
When I go on my local server. I get the html delivered and the bootstrap styles are applied to it. However, I get a 400 bad request error for the style.css and client.js linked in my template.js file.
I really hope someone can help me out on this one...
EDIT
Here is what I see on the developer console :
Your server.js file appears to be inside your dist folder, which means that __dirname would be ./dist instead of ./ like your code seems to be written. What you need to do is something like this:
const path = require('path')
const projectRoot = path.resolve(__dirname, '../')
app.use('static', express.static(projectRoot + '/public'))
app.use('dist', express.static(__dirname))

Image file upload with node and express

Hi i am trying to do an image upload with ajax.so this are my files.
//index.html
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>File Upload showing Upload Progress</title>
<style>
* {
font-family: Verdana;
font-size: 12px;
}
</style>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data" id="MyUploadForm">
<input name="ImageFile" id="imageInput" type="file" />
<input type="submit" id="submit-btn" value="Upload" />
<img src="images/ajax-loader.gif" id="loading-img" style="display:none;" alt="Please Wait"/>
</form>
<div id="output"></div>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.7.1.min.js'></script>
<script type='text/javascript' src='main.js'></script>
</body>
<script type="text/javascript" src="js/jquery.form.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var options = {
target: '#output', // target element(s) to be updated with server response
beforeSubmit: beforeSubmit, // pre-submit callback
resetForm: true // reset the form after successful submit
};
$('#MyUploadForm').submit(function() {
$(this).ajaxSubmit(options); //Ajax Submit form
// return false to prevent standard browser submit and page navigation
return false;
});
});
//function to check file size before uploading.
function beforeSubmit(){
//check whether browser fully supports all File API
if (window.File && window.FileReader && window.FileList && window.Blob)
{
if( !$('#imageInput').val()) //check empty input filed
{
$("#output").html("Are you kidding me?");
return false
}
var fsize = $('#imageInput')[0].files[0].size; //get file size
var ftype = $('#imageInput')[0].files[0].type; // get file type
//allow only valid image file types
switch(ftype)
{
case 'image/png': case 'image/gif': case 'image/jpeg': case 'image/pjpeg':
break;
default:
$("#output").html("<b>"+ftype+"</b> Unsupported file type!");
return false
}
//Allowed file size is less than 1 MB (1048576)
if(fsize>1048576)
{
$("#output").html("<b>"+fsize +"</b> Too big Image file! <br />Please reduce the size of your photo using an image editor.");
return false
}
$('#submit-btn').hide(); //hide submit button
$('#loading-img').show(); //hide submit button
$("#output").html("");
}
else
{
//Output error to older unsupported browsers that doesn't support HTML5 File API
$("#output").html("Please upgrade your browser, because your current browser lacks some new features we need!");
return false;
}
}
</script>
</html>
this is my app.js
var express = require('express'); //Express Web Server
var bodyParser = require('body-parser'); //connects bodyParsing middleware
var formidable = require('formidable');
var path = require('path'); //used for file path
var fs =require('fs-extra'); //File System-needed for renaming file etc
var app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
/* ==========================================================
bodyParser() required to allow Express to see the uploaded files
============================================================ */
app.use(bodyParser({defer: true}));
app.route('/').get(function(req,res)
{
console.log("Hello world");
res.render('index.html');
res.end('done');
});
app.post('/upload', function(req, res) {
res.send('fileinfo: ' + req.files);
});
var server = app.listen(3030, function() {
console.log('Listening on port %d', server.address().port);
});
But I am getting req.files undefined.Can anybody tell why? can anybody have solution for my scnario.Am i do everything correctly.
Here is a quotation from the express-formidable-demo page:
Currently broken due to unknown bug
But, you may parse request body explicitly:
app.post('/upload', function(req, res) {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log(files);
res.send('fileinfo: ' + files);
});
});

Resources