How to handle multiple image upload in Nodejs - node.js

It's about two days I'm trying to upload images using Nodejs and Expressjs (4.0).
I tryed several middlewares so far, like: Formidable, Blueimp, Busboy, Multer...
With few of these I correctly saved a single image on a temporary folder but the problems comes when I try to upload multiple images.
So, my simple controller look like:
exports.postAccountImages = function(req, res, next) {
User.findById(req.user.id, function(err, user) {
console.log(req.files);
});
};
What I receive is always single Objects like:
{
files: {
// data...
}
}
{
files: {
// data...
}
}
But are not inside an array, so I can not manage all the files incoming using for.
I need to change the name to the images and save these on dynamic folder based on user.id name... but it seems to be too tricky.
I can do it one by one, but I wish to do that on multiple images.
Do you know a middleware or how to use correctly one of the ones I already tried to manage multiple files?
EDIT:
I used Dragzone for the client side.
Nothing special here, followed the initial tutorial:
Jade:
#uploader.fileInput
h3 Drop your images here!
Js:
var myDropzone = new Dropzone(document.body, {
url: "/account/images", // Set the url
autoQueue: true,
paramName: "file",
uploadMultiple: true,
autoProcessQueue: true,
clickable: ".fileInput"
});

Hope this solves your question, this is my method to multiple upload file:
Nodejs :
router.post('/upload', function(req , res) {
var multiparty = require('multiparty');
var form = new multiparty.Form();
var fs = require('fs');
form.parse(req, function(err, fields, files) {
var imgArray = files.imatges;
for (var i = 0; i < imgArray.length; i++) {
var newPath = './public/uploads/'+fields.imgName+'/';
var singleImg = imgArray[i];
newPath+= singleImg.originalFilename;
readAndWriteFile(singleImg, newPath);
}
res.send("File uploaded to: " + newPath);
});
function readAndWriteFile(singleImg, newPath) {
fs.readFile(singleImg.path , function(err,data) {
fs.writeFile(newPath,data, function(err) {
if (err) console.log('ERRRRRR!! :'+err);
console.log('Fitxer: '+singleImg.originalFilename +' - '+ newPath);
})
})
}
})
Make sure your has enctype="multipart/form-data"
I hope this gives you a hand ;)

Hope this solves your question.
How To Multiple Image upload using Nodejs And MongoDB
import formidable from 'formidable';
import multiparty from 'multiparty';
import _ from 'lodash'
import fs from 'fs'
async create(req,res){
let form = new multiparty.Form();
form.keepExtensions=true;
form.parse(req,(err,field,files) => {
if(err)
{
return res.status(400).json({
error:'Image Could Not Uploaded'
})
}
// Multiple Image Store into Database
let product = new Product(field)
var imgArray = files.photo;
var photoarray = new Array();
for (var i = 0; i < imgArray.length; i++) {
if(imgArray.size >= 1000000)
{
res.status(401).json({
error:'Image is Less then 1 MB'
})
}
var newPath = './uploads/product/';
var singleImg = imgArray[i];
newPath+= Date.now()+'_'+singleImg.originalFilename;
readAndWriteFile(singleImg, newPath);
photoarray.push(newPath)
}
product.photo=photoarray;
//Comma Separated Value Store into MongoDB
var sizestr = field.size.toString()
var text_arr = sizestr.split(',')
var sizearray = new Array();
for(var i=0;i<text_arr.length;i++)
{
sizearray.push(text_arr[i])
}
product.size=sizearray;
product.name=field.name.toString()
product.save((err,result)=>{
console.log(err)
if(err){
return res.status(400).json({
error:errorHandler(err)
})
}
return res.json(result)
})
});
function readAndWriteFile(singleImg, newPath) {
fs.readFile(singleImg.path , function(err,data) {
fs.writeFile(newPath,data, function(err) {
if (err) console.log('ERRRRRR!! :'+err);
console.log('Fitxer: '+singleImg.originalFilename +' - '+ newPath);
})
})
}
}

Related

Streaming image data from node server results in corrupted file (gridfs-stream)

I decided to post this after extensive searching here (1, 2, 3 ) and here (1, 2) and many, many other related posts. I am loosing hope, but will not give up that easily :)
I'm using multer to upload a PNG image to mongo database:
const storage = new GridFsStorage({
url: 'mongodb://my_database:thisIsfake#hostName/my_database',
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => { // generating unique names to avoid duplicates
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'media',
metadata : {
clientId : req.body.client_id // added metadata to have a reference to the client to whom the image belongs
}
};
resolve(fileInfo);
});
});
}
});
const upload = multer({storage}).single('image');
Then I create a stream and pipe it to response:
loader: function (req, res) {
var conn = mongoose.createConnection('mongodb://my_database:thisIsfake#hostName/my_database');
conn.once('open', function () {
var gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('media');
gfs.files.find({ metadata : {clientId : req.body.id}}).toArray(
(err, files) => {
if (err) throw err;
if (files) {
const readStream = gfs.createReadStream(files[0].filename); //testing only with the first file in the array
console.log(readStream);
res.set('Content-Type', files[0].contentType)
readStream.pipe(res);
}
});
});
}
Postman POST request to end point results in response body being displayed as an image file:
In the front end I pass the response in a File object, read it and save the result in a src attribute of img:
findAfile(){
let Data = {
id: this.$store.state.StorePatient._id,
};
console.log(this.$store.state.StorePatient._id);
visitAxios.post('http://localhost:3000/client/visits/findfile', Data )
.then(res => {
const reader = new FileReader();
let file = new File([res.data],"image.png", {type: "image/png"});
console.log('this is file: ',file);
reader.readAsDataURL(file); // encode a string
reader.onload = function() {
const img = new Image();
img.src = reader.result;
document.getElementById('imgContainer').appendChild(img);
};
})
.catch( err => console.error(err));
}
My File object is similar to the one I get when using input field only bigger:
This is original file:
When inspecting element I see this:
Looks like data URI is where it should be, but it's different from the original image on file input:
Again, when I want to display it through input element:
onFileSelected(event){
this.file = event.target.files[0];
this.fileName = event.target.files[0].name;
const reader = new FileReader();
console.log(this.file);
reader.onload = function() {
const img = new Image();
img.src = reader.result;
document.getElementById('imageContainer').appendChild(img);
};
reader.readAsDataURL(this.file);
}
I get this:
But when reading it from the response, it is corrupted:
Postman gets it right, so there must be something wrong with my front-end code, right? How do I pass this gfs stream to my html?
I managed to make a POST request to fetch an image from MongoDB and save it in the server dir:
const readStream = gfs.createReadStream(files[0].filename);
const wstream = fs.createWriteStream(path.join(__dirname,"uploads", "fileToGet.jpg"));
readStream.pipe(wstream);
Then, I just made a simple GET request by adding an absolute path to the and finally delete the file after successful response:
app.get('/image', function (req, res) {
var file = path.join(dir, 'fileToGet.jpg');
if (file.indexOf(dir + path.sep) !== 0) {
return res.status(403).end('Forbidden');
}
var type = mime[path.extname(file).slice(1)] || 'text/plain';
var s = fs.createReadStream(file);
s.on('open', function () {
res.set('Content-Type', type);
s.pipe(res);
});
s.on('end', function () {
fs.unlink(file, ()=>{
console.log("file deleted");
})
});
s.on('error', function () {
res.set('Content-Type', 'text/plain');
res.status(404).end('Not found');
});

Trouble in using forEach function in node.js

I have been learning about q promises and tried to build up some mock APIs to implement its functionality,While doing so I came across the following error,
Enterprise.forEach is not a function
My API code is as follows,
var mongoose = require('mongoose');
var Enterprise = mongoose.model('Enterprise_gpy');
var q = require('q');
var displayEnterprise = function(req, res) {
function displayEnterpriseName() {
var deferred = q.defer();
Enterprise.forEach(function(err, doc) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var name = Enterprise.enterprise_name;
deferred.resolve({
name: name
});
}
return deferred.promise;
});
}
function displayEnterpriseEmail() {
var deferred = q.defer();
Enterprise.forEach(function(err, doc) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var email = Enterprise.enterprise_email;
deferred.resolve({
email: email
});
}
return deferred.promise;
});
}
q.all([
displayEnterpriseName(),
displayEnterpriseEmail()
])
.then(function(success) {
console.log(500, success);
})
.fail(function(err) {
console.log(200, err);
});
}
module.exports = {
displayEnterprise: displayEnterprise
}
In your code Enterprise is a mongoose schema so when you try to do loop using forEach then got
Enterprise.forEach is not a function
you can use forEach after Enterprise.find(). so use
Enterprise.find({}, function(err, docs) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var names = [];
docs.forEach (function(doc) {
var name = doc.enterprise_name;
names.push(name);// pushed in names array
//.....
});
deferred.resolve({
names: names
}); // return all names
}
});
instead of
Enterprise.find().forEach
and should use
var name = doc.enterprise_name; instead of var name = Enterprise.enterprise_name;
and
var email = doc.enterprise_email; instead of var email = Enterprise.enterprise_email;
forEach only works for arrays, and you're using it on a mongoose model.
try this instead:
Enterprise.find().exec(function(err, docs) {
docs.forEach(function(doc) {
// do something with all the documents
}
// do something outside the loop
})

File upload nodejs to server

I want to upload files of my form to my server.
I have already test this but i haven't a success.
What is the best npm module for that ?
Can i test it in localhost ?
Thanks
For Express Use,
https://www.npmjs.com/package/multer
For Hapi.js
https://gist.github.com/joyrexus/0c6bd5135d7edeba7b87
Hope This Helps!
Using Hapijs
I have done Image upload in one of my projects
I had Used Nginx to define my root location for this file upload.
var mkdirp = require('mkdirp');
var path = require('path');
var mv = require('mv');
exports.imageUpload = function (req, reply) {
var payload = req.payload;
commonImageUpload(payload.uploadFile,urid,function(err,res){
});
}
var commonImageUpload = function (file, idUser, callback) {
if (null != file) {
var extention = path.extname(file.filename);
var extentionsList = [];
extentionsList.push('.jpg');
extentionsList.push('.png');
extentionsList.push('.jpeg');
extentionsList.push('.gif');
var index = extentionsList.indexOf(extention.toLowerCase());
if (index < 0) {
callback(true,"Invalid Media Type");
} else {
var filepath;
filepath = '../cdn/idcard/';
var fname = filepath + idUser + extention;
console.log(fname);
mkdirp(filepath, function (err) {
if (err) {
console.log(err);
callback(true,"Internal Server Error");
}
else {
mv(file.path, fname, function (err) {
});
}
});
}
} else {
callback(true);
}
}
Let me know if this solves your problem.

form uploading through formidable

I'm receiving following files in the server
{"file":{"size":6818,"path":"/tmp/a451340156a9986cd9d208678bdc40cf","name":"test.pdf","type":"application/pdf","mtime":"2014-09-03T15:26:25.733Z"}}
I have file updload handing as follows:
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log(JSON.stringify(files));
// `file` is the name of the <input> field of type `file`
var old_path = files.file.path,
file_size = files.file.size,
file_ext = files.file.name.split('.').pop(),
index = old_path.lastIndexOf('/') + 1,
file_name = old_path.substr(index),
new_path = path.join(process.env.PWD, '/uploads/', file_name + '.' + file_ext);
fs.readFile(old_path, function(err, data) {
fs.writeFile(new_path, data, function(err) {
fs.unlink(old_path, function(err) {
if (err) {
res.status(500);
res.json({'success': false});
} else {
res.status(200);
res.json({'success': true});
}
});
});
});
});
This gives 200 ok but file is not uploaded to desired directory i.e upload/
new_path is returned as /home/abc/myapp/uploads/0bc49fa19d15fb5bdf779c02d3cbc1d5.pdf
however it should just be /uploads/test.pdf
Is it the path or the filename that's causing the issue?
I'd start by simplifying the code by using the rename function. It would look something like this.
var newFilePath = path.join(process.env.PWD, 'uploads', files.file.name);
fs.rename(files.file.path,newFilePath,function(err){
if(err){
//handle error
}
res.json({success: 'true'});
});

How do I render an EJS template file in Node.js?

I'm using Node.js and trying to render an EJS template file. I figured out how to render strings:
var http = require('http');
var ejs = require('ejs');
var server = http.createServer(function(req, res){
res.end(ejs.render('Hello World'));
});
server.listen(3000);
How can I render an EJS template file?
There is a function in EJS to render files, you can just do:
ejs.renderFile(__dirname + '/template.ejs', function(err, data) {
console.log(err || data);
});
Source: Official EJS documentation
var fs = require('fs');
var templateString = fs.readFileSync('template.ejs', 'utf-8');
and then you do your thing:
var server = http.createServer(function(req, res){
res.end(ejs.render(templateString));
});
All you have to do is compile the file as a string (with optional local variables), like so:
var fs = require('fs'), ejs = require('ejs'), http = require('http'),
server, filePath;
filePath = __dirname + '/sample.html'; // this is from your current directory
fs.readFile(filePath, 'utf-8', function(error, content) {
if (error) { throw error); }
// start the server once you have the content of the file
http.createServer(function(req, res) {
// render the file using some local params
res.end(ejs.render(content, {
users: [
{ name: 'tj' },
{ name: 'mape' },
{ name: 'guillermo' }
]
});
});
});
#ksloan's answer is really good. I also had the same use case and did little bit of digging. The function renderFile() is overloaded. The one you will need mostly is:
renderFile(path: string,data, cb)
for example:
ejs.renderFile(__dirname + '/template.ejs', dataForTemplate, function(err, data) {
console.log(err || data)
})
where dataForTemplate is an object containing values that you need inside the template.
There's a synchronous version of this pattern that tightens it up a little more.
var server = http.createServer(function(req, res) {
var filePath = __dirname + '/sample.html';
var template = fs.readFileSync(filePath, 'utf8');
res.end(ejs.render(template,{}));
});
Note the use of readFileSync(). If you specify the encoding (utf8 here), the function returns a string containing your template.
The answer of #ksloan should be the accepted one. It uses the ejs function precisely for this purpose.
Here is an example of how to use with Bluebird:
var Promise = require('bluebird');
var path = require('path');
var ejs = Promise.promisifyAll(require('ejs'));
ejs.renderFileAsync(path.join(__dirname, 'template.ejs'), {context: 'my context'})
.then(function (tpl) {
console.log(tpl);
})
.catch(function (error) {
console.log(error);
});
For the sake of completeness here is a promisified version of the currently accepted answer:
var ejs = require('ejs');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');
fs.readFileAsync(path.join(__dirname, 'template.ejs'), 'utf-8')
.then(function (tpl) {
console.log(ejs.render(tpl, {context: 'my context'}));
})
.catch(function (error) {
console.log(error);
});
Use ejs.renderFile(filename, data) function with async-await.
To render HTML files.
const renderHtmlFile = async () => {
try {
//Parameters inside the HTML file
let params = {firstName : 'John', lastName: 'Doe'};
let html = await ejs.renderFile(__dirname + '/template.html', params);
console.log(html);
} catch (error) {
console.log("Error occured: ", error);
}
}
To render EJS files.
const renderEjsFile = async () => {
try {
//Parameters inside the HTML file
let params = {firstName : 'John', lastName: 'Doe'};
let ejs = await ejs.renderFile(__dirname + '/template.ejs', params);
console.log(ejs);
} catch (error) {
console.log("Error occured: ", error);
}
}

Resources