How to upload multiple images on imageKit from nodeJs app? - node.js

I'm trying to the images submitted through HTML form and then upload those in the imagekit through node app. I'm completely lost with the configuration. Any help would be greatly appreciated.
const app = express();
const multer = require('multer');
const path = require('path');
const upload = multer({
dest: "uploads/" // "uploads"
});
var ImageKit = require("imagekit");
var fs = require('fs');
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.urlencoded({extended: true}));
var imagekit = new ImageKit({
publicKey : "public_6ImvGNsOViPhZ*******",
privateKey : "private_IZ1pjwUR9F********",
urlEndpoint : "https://ik.imagekit.io/*****/"
});
app.get('/upload', (req, res) => {
res.render('index')
})
app.post('/upload', upload.single("image"), (req, res) => {
fs.readFile('uploads/' + req.file.filename, function(err, data) {
if (err) throw err; // Fail if the file can't be read.
imagekit.upload({
file : req.file, //required
fileName : req.file.filename + '.jpg', //required
tags: ["tag1", "tag2"]
}, function(error, result) {
if(error) console.log(error);
else console.log(result);
});
});
console.log(req.file.filename);
res.redirect('/upload');
})

You don't need to send your images to the node app and then upload them, you can directly upload your images from the client-side with a backend authentication endpoint. You can use imagekit-javascript sdk for this.
It also has a sample app which you can refer to.

First setup a middleware for Multer and inside it set the storage destination to memory:
module.exports=(req, res, next) =>{
const uploadFiles = multer({storage:multer.memoryStorage()}).single('image'); //'image' = the input name in will be in the form
uploadFiles(req, res, err => {
if (err instanceof multer.MulterError) { // A Multer error occurred when uploading.
if (err.code === "LIMIT_UNEXPECTED_FILE") { // Too many images exceeding the allowed limit
// ...
}
} else if (err) {
// handle other errors
}
// Everything is ok.
next();
});
}
then in your post request do this:
exports.addImage = async (req, res, next) => {
await imagekit.upload({
file : req.file.buffer, //required
fileName : req.file.originalname, //required
folder:'myFolder', // optional
}, function(error, result) {
if(error) console.log(error);
else
return res.status(200).json({
message:result
})
});
}

Related

Uploading image and video into a form to create an object

Hope you're doing well.
I have a question regarding my Node.js backend app. I am trying to upload an image and a video but I get this error :
"error": "Unexpected field" on postman
Here is my code :
create:(req,res)=>{
console.log("Test1");
console.log("Test1bis");
//const exercise=JSON.parse(req.body.exercise);
//delete exercise._id;
console.log("Test2")
var exerciseB = new Exercise({
...req.body,
image:`${req.protocol}://${req.get('host')}/images/exercise/${req.file.filename1}`,
video:`${req.protocol}://${req.get('host')}/videos/exercise/${req.file.filename2}`
})
console.log("Test3")
exerciseB.save((err,exercise)=>{
if(err){
console.log("C'est le 2eme err");
return res.status(500).json({
status:500,
message:err.message
})
}
console.log("Ca arrive jusqu'au 200");
return res.status(201).json({
status:200,
message:"Exercise Created Successfully !"
})
})
}
And this is the multer file I am using to generate the image in a separate file ( I am using a similar multer for the video) :
const multer=require('multer');
const MIME_TYPES={
'image/jpg':'jpg',
'image/jpeg':'jpg',
'image/png':'png'
}
const storage=multer.diskStorage({
destination:(req,file,callback)=>{
callback(null,'public/images/exercise');
},
filename1:(req,file,callback)=>{
var name=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Date.now()+".";
const extension=MIME_TYPES[file.mimetype];
name+=extension;
callback(null,name)
}
})
module.exports=multer({storage}).single('image');
And here is my postman screenshot :
This is my other multer file for the video :
const multer=require('multer');
const MIME_TYPES={
'video/mp4':'mp4',
'video/mpeg':'mpeg',
'video/ogg':'ogv',
'video/mp2t':'ts',
'video//webm':'webm',
}
const storage=multer.diskStorage({
destination:(req,file,callback)=>{
callback(null,'public/videos/exercise');
},
filename2:(req,file,callback)=>{
var name=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Math.floor(Math.random()*Math.floor(123456789)).toString();
name+=Date.now()+".";
const extension=MIME_TYPES[file.mimetype];
name+=extension;
callback(null,name)
}
})
module.exports=multer({storage}).single('video');
And this is my routes file :
var express = require('express');
var router = express.Router();
var exerciseController = require('../controllers/exerciseController');
const multerImage=require('../middlewares/multer-image');
const multerVideo=require('../middlewares/multer-video');
/* GET home page. */
router.get('/show', exerciseController.show);
router.post('/create',multerImage, multerVideo, exerciseController.create);
router.put('/update/:id',multerImage, exerciseController.update);
module.exports = router;
I think I can't proceed the way I do in the routes file in the second router call.
Hope you can help me ! Thank you for trying too :)
remove this line
module.exports=multer({storage}).single('image');
and set this line because image is not your name filename1/filename2 your image name
module.exports=multer({storage}).single('filename1');
This is running code but make sure you already created dir public/uploads, you edit your upload location and upload multiple file
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer');
const app = express();
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }))
app.use(bodyParser.json({ limit: '10mb', extended: true }))
app.use(express.static('public/uploads'))
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public/uploads")
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
})
app.post('/', (req, res) => {
const upload = multer({ storage: storage }).single('image_url');
upload(req, res, function (err) {
if (err) {
console.log(err)
return
}
var exerciseB = new Exercise({
...req.body,
image: `${req.protocol}://${req.get('host')}/images/exercise/${req.file.originalname}`,
video: `${req.protocol}://${req.get('host')}/videos/exercise/${req.file.originalname}`
})
exerciseB.save((err, exercise) => {
if (err) {
console.log("C'est le 2eme err");
return res.status(500).json({
status: 500,
message: err.message
})
}
console.log("Ca arrive jusqu'au 200");
return res.status(201).json({
status: 200,
message: "Exercise Created Successfully !"
})
})
})
})
app.listen(9001, () => {
console.log(`listening on port ${9001}`)
});

Can not upload image to NodeJs server using Multer

I am trying to upload image to to server and store it in a separate folder. I am submitting multipart form with image attached to it but I am not getting any errors, response is always successfull even when I am trying to submit an empty form. Is there are any suggestions why it is not working?
controller.js
const multer = require('multer');
const shortid = require('shortid');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "/home/username/projects/data/img/"); // Static route to save images
console.log("DESTINATION CB: " + cb);
},
filename: function (req, file, cb) {
cb(null, shortid.generate() +".jpg");
}
});
const maxSize = 10 * 1000 * 1000; // 10 MB Max Picture size
var upload = multer({
storage: storage,
limits: { fileSize: maxSize },
fileFilter: function (req, file, cb){
// Set the filetypes, it is optional
var filetypes = /jpeg|jpg|png/;
var mimetype = filetypes.test(file.mimetype);
var extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Error: File upload only supports the " + "following filetypes - " + filetypes);
}
}).single('image');
module.exports = {
createBlog: function(req, res, next) {
upload(req, res, function(err) {
if (err instanceof multer.MulterError) {
res.json({status: "Error", error_message: "Some kind of error with image"});
} else {
res.json({status: "Success", success_message: "Image uploaded successfully."});
}
});
}
};
route.js
const express = require('express');
const router = express.Router();
const controller = require('../app/api/controllers/controller');
router.post('/blog/create', controller.createBlog)
module.exports = router;
server.js
const express = require('express');
const logger = require('morgan');
const route = require('./routes/route');
const bodyParser = require('body-parser');
const mongoose = require('./config/database'); // DB configuration
const app = express();
// Connection to mongodb
mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:'));
mongoose.set('useFindAndModify', false);
app.use(bodyParser.json({limit: "50mb"}));
app.use(logger('dev'));
app.use(bodyParser.urlencoded({limit: "50mb", extended: true, parameterLimit: 50000}));
app.use('/blogx', route);
app.get('/favicon.ico', function(req, res) {
res.sendStatus(204);
});
// Handle 404 error
app.use(function(req, res, next) {
let err = new Error('Not Found!');
err.status = 404;
next(err);
});
// Handle errors
app.use(function(err, req, res, next) {
console.log(err);
if (err.status === 404) {
res.status(404).json({message: "Not Found!"});
} else {
res.status(500).json({message: "Something looks wrong :("});
}
});
app.listen(3001, function(){
console.log('MAIN Server is listening on port 3001');
});
And this is how I am trying to submit form using Insomnia
request
You probably need to make sure that the upload directory /home/username/projects/data/img/ exists before starting the server/executing the request.
Note that you would have been able to detect this kind of error yourself, if your error logic also had handled non-multer errors, as in this case instead of a multer error a system error (NOENT) is thrown :
upload(req, res, function(err) {
if (err instanceof multer.MulterError) {
res.json({status: "Error", error_message: "Some kind of error with multer"});
} else if(err) {
res.json({status: "Error", error_message: err.message});
} else {
res.json({status: "Success", success_message: "Image uploaded successfully."});
}
});

In a NodeJS / Express (& multer) attachment upload utility, how to handle API delete requests?

The below is a rudimentary file upload utility. It handles React / Axios API POSTs satisfactorily. Files get uploaded to the ~uploads folder under root on the server. How does one add API DELETE handling capability to it? Envision a use case where a user uploads an attachment to a blog post and then deletes the attachment from the blog post. Have had issues locating an example.
var cors = require('cors');
var express = require('express');
var multer = require('multer')
var app = express();
app.use(cors());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
var storage = multer.diskStorage(
{
destination: function (req, file, cb)
{
cb(null, 'fileuploads');
},
filename: function (req, file, cb)
{
cb(null, file.originalname );
}
})
var upload = multer({ storage: storage }).array('file')
app.post('/upload',function(req, res)
{
upload(req, res, function (err)
{
if (err instanceof multer.MulterError)
{
return res.status(500).json(err)
}
else if (err)
{
return res.status(500).json(err)
}
return res.status(200).send(req.file)
})
});
app.listen(8001, function() {
console.log('App running on port 8001');
});
EDIT:
Modified app.delete(...) to the below, which successfully deletes the file but after about a minute throws this error in the console: [Error: ENOENT: no such file or directory, unlink '<PATH VALUE>']
app.delete('/writer/:file_to_delete', async (req, res) =>
{
const path = 'assets/uploads/' + req.params.targetFile;
console.log(path);
try
{
fs.unlink(path)
// NOTE: `fs.unlinkSync(path)` does the same thing
console.log('File deleted!');
return res.status(200);
}
catch(err)
{
console.error(err)
return res.status(500).json(err)
}
});
I found a way forward. The code is below but I'm happy to hear about better ways to do it:
// NOTE: `file_name` values are obfuscated and uniquely generated by the client.
// The actual file name is in data storage.
app.post('/delete/:file_name', (req, res) =>
{
const theFile = 'attachments/' + req.params.file_name;
var resultHandler = function(err) {
if(err) {
console.log("file delete failed", err);
return res.status(500).json(err)
}
console.log("file deleted");
return res.status(200).send({data: req.params.file_name + ' deleted'});
}
fs.unlinkSync(theFile, resultHandler);
});

How to use multiple engines (ejs and hbs) with Node.js and Express

I am trying to link (href) my hbs page to a ejs page in my views folder. I have installed consolidate as seen in other tutorials via going to the directory npm install consolidate and have added the code var engines = require('consolidate'); and app.engine('handlebars', engines.handlebars); to my code but nothing works (I also know if you’re using “.ejs” extensions you don’t need to do anything). Can someone please help me out as I cannot view my pages due to this. I receive this error - "Failed to lookup view employee/addOrEdit in views directory...". I have added my folder/file structure below and code snippets. Thank you!
folder/file structure
server.js file
require('./models/db');
const express = require('express');
const path = require('path');
const exphbs = require('express-handlebars');
const bodyparser = require('body-parser');
var engines = require('consolidate');
const employeeController = require('./controllers/employeeController');
var app = express();
app.engine('handlebars', engines.handlebars);
app.use(bodyparser.urlencoded({
extended: true
}));
app.use(bodyparser.json());
app.set('views', path.join(__dirname, '/views/'));
app.engine('hbs', exphbs({ extname: 'hbs', defaultLayout: 'mainLayout', layoutsDir: __dirname + '/views/layouts/' }));
app.set('view engine', 'hbs');
//render css files
app.use(express.static("public"));
app.listen(3000, () => {
console.log('Express server started at port : 3000');
});
app.use('/employee', employeeController);
employeeController.js file
const express = require('express');
var router = express.Router();
const mongoose = require('mongoose');
const Employee = mongoose.model('Employee');
//placeholders for added task
var task = ["complete presentation", "practise with nodejs"];
//placeholders for removed task
var complete = ["finish jquery"];
router.get('/', (req, res) => {
res.render("employee/addOrEdit", {
viewTitle: "Insert Module"
});
});
router.get('/test', (req, res) => {
res.render("employee/test");
});
//render the ejs and display added task, completed task
router.get("/index", function(req, res) {
res.render("index", { task: task, complete: complete });
});
router.post('/', (req, res) => {
if (req.body._id == '')
insertRecord(req, res);
else
updateRecord(req, res);
});
function insertRecord(req, res) {
var employee = new Employee();
employee.fullName = req.body.fullName;
employee.module = req.body.module;
employee.mobile = req.body.mobile;
employee.city = req.body.city;
employee.save((err, doc) => {
if (!err)
res.redirect('employee/list');
else {
if (err.name == 'ValidationError') {
handleValidationError(err, req.body);
res.render("employee/addOrEdit", {
viewTitle: "Insert Module",
employee: req.body
});
}
else
console.log('Error during record insertion : ' + err);
}
});
}
function updateRecord(req, res) {
Employee.findOneAndUpdate({ _id: req.body._id }, req.body, { new: true }, (err, doc) => {
if (!err) { res.redirect('employee/list'); }
else {
if (err.name == 'ValidationError') {
handleValidationError(err, req.body);
res.render("employee/addOrEdit", {
viewTitle: 'Update Module',
employee: req.body
});
}
else
console.log('Error during record update : ' + err);
}
});
}
router.get('/list', (req, res) => {
Employee.find((err, docs) => {
if (!err) {
res.render("employee/list", {
list: docs
});
}
else {
console.log('Error in retrieving module list :' + err);
}
});
});
function handleValidationError(err, body) {
for (field in err.errors) {
switch (err.errors[field].path) {
case 'fullName':
body['fullNameError'] = err.errors[field].message;
break;
case 'module':
body['moduleError'] = err.errors[field].message;
break;
default:
break;
}
}
}
router.get('/:id', (req, res) => {
Employee.findById(req.params.id, (err, doc) => {
if (!err) {
res.render("employee/addOrEdit", {
viewTitle: "Update Module",
employee: doc
});
}
});
});
router.get('/delete/:id', (req, res) => {
Employee.findByIdAndRemove(req.params.id, (err, doc) => {
if (!err) {
res.redirect('/employee/list');
}
else { console.log('Error in module delete :' + err); }
});
});
//post route for adding new task
router.post("/addtask", function(req, res) {
var newTask = req.body.newtask;
//add the new task from the post route
task.push(newTask);
res.redirect("/");
});
router.post("/removetask", function(req, res) {
var completeTask = req.body.check;
//check for the "typeof" the different completed task, then add into the complete task
if (typeof completeTask === "string") {
complete.push(completeTask);
//check if the completed task already exits in the task when checked, then remove it
task.splice(task.indexOf(completeTask), 1);
} else if (typeof completeTask === "object") {
for (var i = 0; i < completeTask.length; i++) {
complete.push(completeTask[i]);
task.splice(task.indexOf(completeTask[i]), 1);
}
}
res.redirect("/");
});
module.exports = router;
You need to add both engines, ejs and express-handlebars in your package.json file
npm i ejs
npm i express-handlebars
Import dependency in Server.js file
const ejs = require('ejs');
const exhbs = require('express-handlebars');
Set engine to your server.js file
app.set('view engine', 'ejs');
app.engine('handlebars', exhbs());
app.set('view engine', 'handlebars');
For rendering template, you need to set the file extension
res.render('heyy.handlebars');
res.render('index.ejs');
This is your Folder Structure
Inside main.handlebars and inside body add {{{body}}} inorder to mark where layout should insert page

Error handling when uploading file using multer with expressjs

I am using multer to save the file on server developed through express & nodejs.
I am usign following code.
var express = require('express'),
multer = require('multer')
var app = express()
app.get('/', function(req, res){
res.send('hello world');
});
app.post('/upload',[ multer({ dest: './uploads/'}), function(req, res){
res.status(204).end()
}]);
app.listen(3000);
Multer saves the file for me in the specified destination folder.
All this is working fine but I have following questions:
If the file saving fails for various reasons, it looks like my route will always return status 204.
I am not sure if status 204 is retured after file is saved or while the file is getting saved asynchronously, status 204 is returned.
This is how to write multer middleware that handle upload and errors
const multer = require("multer");
function uploadFile(req, res, next) {
const upload = multer().single('yourFileNameHere');
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
next()
})
}
You can handle errors using the onError option:
app.post('/upload',[
multer({
dest : './uploads/',
onError : function(err, next) {
console.log('error', err);
next(err);
}
}),
function(req, res) {
res.status(204).end();
}
]);
If you call next(err), your route handler (generating the 204) will be skipped and the error will be handled by Express.
I think (not 100% sure as it depends on how multer is implemented) that your route handler will be called when the file is saved. You can use onFileUploadComplete to log a message when the upload is done, and compare that to when your route handler is called.
Looking at the code, multer calls the next middleware/route handler when the file has been uploaded completely.
Try this
var upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
return
}
// Everything went fine
})
}
ref :
http://wiki.workassis.com/nodejs-express-get-post-multipart-request-handling-example/
https://www.npmjs.com/package/multer#error-handling
As you can see from the code below (the source from the muter index.js file), if you not pass the onError callback the error will be handled by express.
fileStream.on('error', function(error) {
// trigger "file error" event
if (options.onError) { options.onError(error, next); }
else next(error);
});
Be careful with the system when the user sends anything to you
I usually set more [*option1]:
process.on('uncaughtException', function(ls){
// console.log(ls);
(function(){})();
});
And then:
var upload= multer({ dest: __dirname + '/../uploads/' }).single('photos');
// middle ..
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
console.log('MulterError', err);
} else if (err) {
// An unknown error occurred when uploading.
// Work best when have [*option1]
console.log('UnhandledError', err);
}
if(err) {
return res.sendStatus(403);
}
res.sendStatus(200);
});
package: "multer": "^1.4.2"
var multer = require('multer')
var upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
handle error
} else if (err) {
handle error
}
else{
write you code
}
})
})
you can see this from the documentation
According to the multer documentation (https://github.com/expressjs/multer#error-handling)
Error handling
When encountering an error, Multer will delegate the error to Express. You can display a nice error page using the standard express way.
If you want to catch errors specifically from Multer, you can call the middleware function by yourself. Also, if you want to catch only the Multer errors, you can use the MulterError class that is attached to the multer object itself (e.g. err instanceof multer.MulterError).
code sample
const multer = require('multer')
const upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
})
})
When we re-write the code in this question with latest version of multer (v1.4.5-lts.1)
const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer({ dest: './uploads/' }).single('fieldName');
app.get('/', (req, res) => {
res.send('hello world');
});
app.post(
'/upload',
(req, res, next) => {
upload(req, res, (err) => {
if (err instanceof multer.MulterError) {
res.status(404).send(err + 'Upload failed due to multer error');
} else if (err) {
res.status(404).send(err + 'Upload failed due to unknown error');
}
// Everything went fine.
next();
});
},
(req, res) => {
res.status(204).end();
}
);
app.listen(3000);
To check the Multer errors and non multer errors we can add validations using fileFilter and limits
eg:
I am adding a CSV file filter method and some limits
// CSV file filter - will only accept files with .csv extension
const csvFilter = (req, file, cb) => {
console.log('csv filter working');
if (file.mimetype.includes('csv')) {
cb(null, true);
} else {
cb('Please upload only csv file.', false);
}
};
// adding the csv file checking, file number limit to 1 and file size limit 10 1kb
const upload = multer({
dest: './uploads/',
fileFilter: csvFilter,
limits: { files: 1, fileSize: 1024 }
}).single('fieldName');
We can see different errors are thrown when we try to upload non CSV file or >1kb sized file or multiple files.
I know its late but it might help others.
Here is how I handle errors and use it safely in my express/typescript
project.
const upload = (fieldName: string) => {
return (req: Request, res: Response, next: NextFunction) => {
return multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
if (file.fieldname === 'post') {
return cb(null, `${path.join(path.dirname(__dirname), 'uploads/postImg')}`);
} else if (file.fieldname === 'profile') {
return cb(null, `${path.join(path.dirname(__dirname), 'uploads/ProfilePic')}`);
} else {
return cb(new Error(`${file.fieldname} is incorrect`), null);
}
},
filename: (req, file, cb) => {
return cb(null, `${file.originalname}-${Date.now()}-${file.fieldname}`);
},
}),
fileFilter: (req, file, cb) => {
const fileExtension = file.mimetype.split('/')[1];
if (!(fileExtension in allowedFiles)) return cb(null, false);
return cb(null, true);
},
dest: `${path.join(path.dirname(__dirname), 'uploads')}`,
limits: {
fileSize: 1024 * 1024 * 3, // 3MB
files: 1,
},
}).single(fieldName)(req, res, (err: any) => {
if (err instanceof multer.MulterError) {
// handle file size error
if (err.code === 'LIMIT_FILE_SIZE') return res.status(400).send({ error: err.message });
// handle unexpected file error
if (err.code === 'LIMIT_UNEXPECTED_FILE') return res.status(400).send({ error: err.message });
// handle unexpected field key error
if (err.code === 'LIMIT_FIELD_KEY') return res.status(400).send({ error: err.message });
}
next();
});
};
};
app.post("/upload", (req: Request, res:Response)=>{
res.json({message:"file uploaded"})
})

Resources