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}`)
});
Related
I want to build an API "/upload/excel" that will allow users to import an excel file and inside it after receiving an excel file, it will read its field and save it into database.
How to achieve this?
I am using multer for uploading the file and xlsx to process it and mongodb as a database (mongoose for the model):
Lead is the data model, you have to add the Excel sheet columns name. See mongoose documentation for more information
const express = require("express");
const multer = require("multer");
const connectDB = require("./config/db");
const Lead = require("./models/Lead");
connectDB();
const uploadXLSX = async (req, res, next) => {
try {
let path = req.file.path;
var workbook = XLSX.readFile(path);
var sheet_name_list = workbook.SheetNames;
let jsonData = XLSX.utils.sheet_to_json(
workbook.Sheets[sheet_name_list[0]]
);
if (jsonData.length === 0) {
return res.status(400).json({
success: false,
message: "xml sheet has no data",
});
}
let savedData = await Lead.create(jsonData);
return res.status(201).json({
success: true,
message: savedData.length + " rows added to the database",
});
} catch (err) {
return res.status(500).json({ success: false, message: err.message });
}
};
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads");
},
filename: function (req, file, cb) {
cb(null, Date.now() + "-" + file.originalname);
},
});
const upload = multer({ storage: storage });
app.post("/upload", upload.single("xlsx"), uploadXLSX);
const port = process.env.PORT || 5000;
const server = app.listen(port, () => {
console.log("app running on port", port);
});
So from here when you make a call to localhost:5000/upload with postman see picture below
You can upload a file through multer or formidable
https://www.npmjs.com/package/multer
https://www.npmjs.com/package/formidable
And you can read xl files though any one of these below npm packges
https://www.npmjs.com/package/xlsx
https://www.npmjs.com/package/read-excel-file
My express server is not responding to any of the route requests, whatever I do no luck.
following are the routes I am trying to access:
http://localhost:3000/api/files
http://localhost:3000/files/<:uuid>
http://localhost:3000/api/files/send
Terminal output:
Response via insomia:
Following are the files of the project
server.js
const express = require('express');
const app = express();
const path = require('path');
app.use(express.static('public'));
app.use(express.json);
const PORT = process.env.PORT||3000;
const connectDB = require('./config/db');
connectDB();
app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'ejs');
app.use('/api/files', require('./routes/files'));
app.use('/files', require('./routes/show'));
app.use('/files/download', require('./routes/download'));
console.log("Server");
app.listen(PORT, () =>{
console.log(`Listening on port ${PORT}`);
});
routes/files.js
const router = require('express').Router();
const multer = require('multer');
const path = require('path');
const File = require('../models/file');
const {v4 : uuid4} = require('uuid');
const sendMail = require('../services/email');
require('dotenv').config();
let storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'uploads/'),
filename: (req, file, cb) => {
const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1E9)}${path.extname(file.originalname)}`;
cb(null, uniqueName)
},
});
let upload = multer({
storage:storage,
limit: { fileSize: 100000*200},
}).single('myfile');
router.post('/', (req, res) => {
console.log("Reached here");
console.log(req);
// store locally
upload(req, res, async (err) => {
// check for valid req
if(!req.file){
return res.json({
error: "Error processing file!"
});
}
if(err){ return res.status(500).send({error: err.message})}
// store in db
const file = File({
filename: req.file.filename,
uuid: uuid4(),
path: req.file.path,
size: req.file.size,
});
const response = await file.save();
return res.json({file: `${process.env['APP_BASE_URL']}/files/${response.uuid}`})
});
//return link
});
router.post('/send', async (req, res) => {
console.log(req.body);
const {uuid, emailTo, emailFrom} = req.body;
if(!uuid || !emailTo || !emailFrom){ return res.status(422).send({error: 'All files are required.'});}
const file = await File.findOne({uuid: uuid});
if(file.sender){
return res.status(422).send({error: 'Email already sent.'});
}
file.sender = emailFrom;
file.receiver = emailTo;
const response = await file.save();
sendMail({
from: emailFrom,
to: emailTo,
subject: "shareMe File Sharing",
text: `${emailFrom} shared a file with you`,
html: require('../service/template')({
emailFrom: emailFrom,
downloadLink: `${process.env['APP_BASE_URL']}/files/${file.uuid}`,
size: parseInt(file.size/1000)+'KB',
expires: '24 hours'
})
});
return res.send({success: true});
});
module.exports=router;
routes/show.js
const router = require('express').Router();
const File = require('../models/file');
router.get('/:uuid', async (req, res) => {
try{
const file = await File.findOne({
uuid: req.params.uuid
});
if(!file){
return res.render('download', {error: 'File not fetched.'});
}
console.log(`${process.env['APP_BASE_URL']}/files/download/${file.uuid}`);
return res.render('download', {
uuid: file.uuid,
fileName: file.filename,
size: file.size,
downloadLink: `${process.env['APP_BASE_URL']}/files/download/${file.uuid}`,
});
}catch(err){
return res.render('download', {error: 'Something seems to be upset :('});
}
});
module.exports = router;
routes/download.js
const router = require('express').Router();
const File = require('../models/file');
router.get('/:uuid', async (req, res) => {
// Extract link and get file from storage send download stream
const file = await File.findOne({ uuid: req.params.uuid });
// Link expired
if(!file) {
return res.render('download', { error: 'Link has been expired.'});
}
const response = await file.save();
const filePath = `${__dirname}/../${file.path}`;
res.download(filePath);
});
module.exports = router;
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."});
}
});
I am new in ExpressJs and working on creating api for one of a dashboard created in reactjs. There is a form in a dashboard which is collecting some of information from the users like "title", "description" and "image". I have created an express server to collect that information and to save it into mongodb. For images What I have done is that, I am uploading image to Cloudinary and storing uploaded url and public_id into database.
So after following some of tutorials I have done something like this.
index.js
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const db = require("./db");
// Api router import goes here
const sectionTypesRouter = require("./routes/section-types-router");
const app = express();
const apiPort = 3000;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(bodyParser.json());
db.on("error", console.error.bind(console, "MongoDB connection error:"));
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.use("/api", sectionTypesRouter);
app.listen(apiPort, () => console.log(`Server running on port ${apiPort}`));
Than, First I have create a file multer.js :
const multer = require("multer");
const storage = multer.diskStorage({
destination: "public/uploads",
filename: (req, file, cb) => {
cb(null, file.fieldname + "-" + Date.now());
},
});
const fileFilter = (req, file, cb) => {
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
//reject file
cb({ message: "Unsupported file format" }, false);
}
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
});
module.exports = upload;
Below is my api router section-type-router.js :
const express = require("express");
const upload = require("../utils/multer");
const SectionTypesCtrl = require("../controllers/section-types-ctrl");
const router = express.Router();
router.post(
"/section-type",
upload.single("image"),
SectionTypesCtrl.createSectionType
);
router.get("/section-types", SectionTypesCtrl.getSectionTypes);
module.exports = router;
This is the section-type-ctrl.js :
const SectionType = require("../models/section-type-model");
const fs = require("fs");
const path = require("path");
const cloudinaryUploader = require("../utils/cloudinaryUploader");
const createSectionType = async (req, res) => {
const body = req.body;
if (!body) {
return res.status(400).json({
success: false,
error: "Required parameter are missing",
});
}
cloudinaryUploader
.uploads(req.file.path, "Section-Types")
.then((result) => {
const sectionType = new SectionType({
title: body.title,
description: body.description,
image: {
url: result.url,
publicId: result.public_id,
},
});
sectionType
.save()
.then(() => {
return res.status(201).json({
success: true,
id: sectionType._id,
message: "Section type created!",
});
})
.catch((error) => {
return res.status(400).json({
error,
message: "Section type not created!",
});
});
})
.catch((error) => {
res.status(500).send({
message: "failure",
error,
});
});
};
module.exports = {
createSectionType,
};
And lastly this is cloudinaryUpload.js :
const cloudinary = require("../config/cloudinary");
exports.uploads = (file, folder) => {
return new Promise((resolve) => {
cloudinary.uploader.upload(
file,
{
resource_type: "auto",
folder: folder,
},
(err, result) => {
if (!err) {
resolve({
url: result.url,
public_id: result.public_id,
});
} else {
throw err;
}
}
);
}).catch((error) => {
throw error;
});
};
Now, everything is working properly. Images is uploading to the cloudinary and returned url and public_id is storing in database. But the problem is that image that I have uploaded is also upload on local directory public/uploads/. This will may create a storage issue while host a site. So Is there any best way to upload image directly to the cloudinary without creating a copy in local directory which also should work on production mode ?
In your example, the file is being stored to public/uploads on your server because you're telling multer to do so via multer.diskStorage
As #Molda's comment above says, you can avoid this by using the multer-storage-cloudinary package to have Multer store the file in Cloudinary automatically.
Another possibility is to change how you're using Multer so it doesn't store the file anywhere, then take the uploaded file while it's in memory and pass it to Cloudinary's SDK as a stream.
There's an example of this in this blog post on the Cloudinary site: https://cloudinary.com/blog/node_js_file_upload_to_a_local_server_or_to_the_cloud
In your case, you can stop using multer.diskStorage, in favour of just using multer() then use streamifier or another library to turn the uploaded file into a stream, and pass that to cloudinary.uploader.upload_stream()
I am trying to create a user profile in express.js and MongoDB. I am using multer for image uploading. Multer middleware always uploads the image before verifying my user data. If user validation is failed, nevertheless image is uploaded. But, I want to upload an image after validating user data. That means, I will check user data in the controller, and if it is valid then I will upload image and store user data to MongoDB. How can I do that? Thanks in advance!
multerConfig.js
exports.multerConfig = (multer) => {
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads/');
},
filename: (req, file, cb) => {
cb(null, 'img-' + new Date().toISOString() + '-' + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
(file.mimetype === 'image/png' || file.mimetype === 'image/jpeg' || file.mimetype === 'image/jpg')
? cb(null, true)
: cb(null, false)
};
return multer({
storage: storage,
limits: { fileSize: 1048576 },
fileFilter: fileFilter
});
};
user.js(Routes)
const multer = require('multer');
const express = require('express');
const userController = require('../controllers/user');
const { multerConfig } = require('../utility/multerConfig');
const validateObjectId = require('../middleware/validateObjectId');
const asyncErrorHandler = require('../middleware/asyncErrorHandler');
const router = express.Router();
const upload = multerConfig(multer);
router.post('/', upload.single('image'), asyncErrorHandler(userController.createUser));
module.exports = router;
user.js(Controller)
const { User, validate } = require('../models/user');
const { deleteFile } = require('../utility/fileUtility');
const { failed, success } = require('../utility/utility');
exports.createUser = async (req, res) => {
const { error } = validate(req.body);
if (error) return res.status(400).send({ ...failed, message: error.details[0].message });
const { name, address, mobile, email, password } = req.body;
if (!req.file) return res.status(400).send({ ...failed, message: `you have to upload an image!` });
const isUserExist = await User.find().or([{ mobile }, { email }]);
if (isUserExist.length > 0) return res.status(409).send({ ...failed, message: `${name} is already exists!` });
const image = req.file.path;
const newUser = new User({ name, address, mobile, email, password, image });
const savedUser = await newUser.save();
if (!savedUser) return res.status(500).send({ ...failed, message: `user ${name} is failed to save!` });
res.send({
...success,
data: savedUser,
message: `user ${name} is saved successfully`
});
};
You can use two multer middleware (one for parsing text, one for uploading your file).
Let's say you have a form with a name (text field) and avatar (file field), you can do this:
var express = require('express');
var multer = require('multer');
var app = express();
var upload = multer({ dest: 'uploads/' });
app.post('/profile',
upload.none(), function (req, res, next) {
// validate `req.body.name` here
// and call next(err) if it fails
next();
},
upload.single('avatar'), function (req, res, next) {
// file is now uploaded, save the location to the database
res.end('done!');
});
app.listen(9000);