Express Multer File Upload - node.js

I am trying to use Multer file upload in my React/Express application. But, I am getting an error that says that the file object is undefined. I have moved all of my Express server api functions into their own file, which has been working fine. I would like to keep the file upload API function in the same file as the rest of the API functions. This is what I have:
server.js
const express = require("express");
var cors = require("cors");
const config = require('config');
const { errorHandler } = require("./middleware/errorMiddleware");
const PORT = config.get('app_port') || 8000;
const app = express();
app.use(express.json());
app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use("/api/meshnodes", require("./routes/myCrudRoutes"));
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
app.use(errorHandler);
myCrudRoutes.js
const express = require('express')
const router = express.Router()
const {uploadFile} = require('../controllers/myCrudContoller')
router.post('/catalog/files/upload/', uploadFile)
module.exports = router
myCrudControllers.js
const ansyncHandler = require("express-async-handler");
const multer = require("multer");
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads");
},
filename: function (req, file, cb) {
let extension = getFileExtention(file.mimetype);
cb(null, file.fieldname + "-" + Date.now() + "." + extension);
},
});
const upload = multer({ storage: storage });
const uploadFile = (upload.single("File"), (req,res, next)=>{
console.log("got file2 ")
const file = req.body;
console.log(req)
if (!file) {
const error = new Error("No File");
error.httpStatusCode = 400;
return next(error);
}
console.log("server upload ")
});
I believe the issue is with my myCrudControllers.js uploadFile function. If I have this same functionality placed directly in my server.js file, like this:
app.post(
"/catalog/files/upload",
upload.single("File"),
(req, res, next) => {
const file = req.file;
//...
It works fine, but I want to be consistent in where I have my API functions.
thanks

I refactored my code and this works:
First, I created a helper file:
uploader.js
const multer = require('multer');
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, process.env.REACT_APP_UPLOAD_LOCATION);
},
filename: function (req, file, cb) {
let extension = getFileExtention(file.mimetype);
cb(null, file.fieldname + "-" + Date.now() + "." + extension);
},
});
const upload = multer({ storage: storage });
myCrudRoutes.js
const uploadHelper = require('../helpers/uploader');
router.post('/catalog/files/upload', uploadHelper.upload.single('File'), uploadFile);
myCrudControllers.js
const uploadFile = ansyncHandler(async (req,res)=>{
const file = req.file;
if (!file) {
const error = new Error("No File");
error.httpStatusCode = 400;
return error;
}
//other stuff
}, (error, req, res, next) => {
res.status(400).send({ error: error.message });
}
);

Related

how do I receive an image that I've uploaded to my server using multer and nodejs in my angular app?

Please I'm new to Nodejs and I'm trying to create an image uploader that will upload files to my server using Nodejs and multer, but the problem is in getting the image back to be displayed in my angular app.
This is the backend code:
const express = require('express');
const multer = require('multer');
const cors = require('cors');
const app = express();
var corsOptions = {
origin: "*",
optionsSuccessStatus: 200,
}
app.use(cors(corsOptions));
app.use(express.static('uploads'));
const 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 });
app.post('/file', upload.single('file'), (req, res) => {
const file = req.file;
if (file) {
res.json(file);
} else {
throw new Error('File upload unsuccessful')
}
})
const port = 3000;
app.listen(port, () => console.log(`Server running on port ${3000}`));
This is my app.html code:
<input type="file" name="image" (change)="upload($event)">
This is my app.ts code:
upload(event: any) {
const file = event.target.files[0];
const formdata = new FormData();
formdata.append('file', file)
this.httpClient.post('http://localhost:3000/file', formdata)
.subscribe((data) => {
console.log(data);
},
(error) => {
console.log(error)
})
Please help me retrieve the image so that I can use it in my angular app. Thank you.
There are two ways you can achieve this. Both the approaches have their own pros and cons.
Store the image locally and send the URL back to the browser.
if (req.files) {
const fileNames = [];
for (let i = 0; i < req.files.length; i++) {
const file = req.files[i];
const relPath = "your/img/path";
const dirName = path.join(BASE_APP_PATH, relPath);
const relFileName = path.join(
relPath,
`${i + 1}_${file.originalname.replace(",", "")}`
);
const img_location = `${dirName}/${
i + 1
}_${file.originalname}`;
if (!fs.existsSync(dirName)) fs.mkdirSync(dirName, { recursive: true });
fs.writeFileSync(img_location, file.buffer, {});
fileNames.push(relFileName);
}
}
Get the image and send back base64 to the browser.
const encoded = req.files[0].buffer.toString('base64')

TypeError: that.getDestination is not a function

I am trying to use multer for the first time to upload images from node.js to mongodb and I was running a test code as shown below, to see if everything works, i followed the documentation and i can't seem to figure out what the issue is.
the full error is this :
TypeError: that.getDestination is not a function at DiskStorage._handleFile
const router = require("express").Router();
const multer = require("multer");
const storage = multer.diskStorage({
destination: {
function(req, file, callback) {
callback(null, "./uploads/");
},
},
filename: {
function(req, file, callback) {
callback(null, new Date.now + file.originalname);
},
},
});
const upload = multer({ storage:storage });
router.post("/images", upload.single("upload"), (req, res) => {
res.send(req.file);
});
module.exports = router;
multer({ storage:storage });
switch this for
multer({ storage });
I was in the same situation as you.
from what I understand, you want to upload a file to place it in ./upload/ with as name ${Date}${Filename}.
with the code i did i got what you wanted i hope this will help you
const router = require('express').Router();
const multer = require('multer');
const path = require('path');
// this create a path like './uploads/'
const uploadsDir = path.resolve(__dirname, 'uploads');
const storage = multer.diskStorage(
{
destination: uploadsDir,
filename: (req, file, cb) => {
cb(null, `${Date.now()}${file.originalname}`);
},
},
);
const upload = multer({ storage });
router.post("/images", upload.single("upload"), (req, res) => {
res.send(req.file);
});
module.exports = router;
if you want more info i found this in a github issue : https://github.com/expressjs/multer/issues/280
const express = require('express');
const app = express()
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination : (req,file,cb)=>{
cb(null,"./Uploads/")
},
filename : (req,file,cb)=>{
const fileExt = path.extname(file.originalname);
// imp file.jpg
const fileName = file.originalname
.replace(fileExt,"")
.toLowerCase()
.split(" ")
.join("-")+"-"+Date.now();
cb(null, fileName + fileExt)
}
})
const multerObj = multer({
storage : storage, // I did (dist : storage) instead of (storage : storage) do this hope you will get your result.
limits : {fileSize : 100000000},
fileFilter : (req,file,cb)=>{
if(file.mimetype === 'image/jpg' ||
file.mimetype === "image/jpeg" ||
file.mimetype === 'immage/png' ||
file.mimetype === 'application/pdf'){
cb(null,true)
}else(
cb(new Error('Only jpg,jpeg,png file allowed.'))
)
}
});
Read the comment with code carefully and do this hope you will get your desire result.

"message": "ENOENT: no such file or directory, open 'E:\\astrology\\utils\\uploads\\1600798534862qf.png'"

As the title suggests, I am getting error: {"message": "ENOENT: no such file or directory, open 'E:\\astrology\\utils\\uploads\\1600798534862qf.png'"} in my project even after passing every required configs.
Note: I've divided 'app' into two parts: main 'app.js' and 'appRoute.js' for more dynamic and code clarity.
app.js
const express = require("express");
const path = require("path");
const app = express();
const directory = path.join(__dirname, "utils/uploads");
app.use("/uploads", express.static(directory));
require("./config/database/db")();
require("./config/approutes/appRoutes")(app);
module.exports = app;
appRoute.js
require("dotenv").config();
const morgan = require("morgan");
const bodyParser = require("body-parser");
const cors = require("cors");
const productRoutes = require("../../api/routes/products");
module.exports = function (app) {
app.use(morgan("dev"));
app.use(bodyParser.urlencoded({ extended: true, limit: "100mb" }));
app.use(bodyParser.json());
app.use(cors({ credentials: true, origin: true }));
app.use("/products", productRoutes);
app.use((req, res, next) => {
const error = new Error("Not found");
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
console.log('SHOW ERROR', error);
res.status(error.status || 500);
res.json({
error: {
message: error.message,
},
});
});
};
fileUpload.js
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, __dirname + "/uploads");
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname.replace(/\s+/g, "-"));
},
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage,
limits: {
fileSize: 1024 * 1024 * 5,
},
fileFilter: fileFilter,
});
module.exports = upload;
Product.js (controller)
exports.create_product = async (req, res, next) => {
const { title, min_content, content } = req.body;
console.log("req files", req.files);
try {
const product = new Product({
title,
min_content,
content,
});
const new_product = await product.save();
console.log("error caught", new_product);
if (new_product) {
res.status(201).json({ msg: "New product added", new_product });
} else {
res.status(400).json({ msg: "Unable to create new product" });
}
} catch (error) {
res.status(500).json({ msg: "Internal server error", error });
}
};
Product.js (route)
const express = require("express");
const router = express.Router();
const ProductController = require("../controllers/products");
const uploadMW = require("../middleware/fileUpload");
router.get("/all", ProductController.get_products);
router.post("/new", uploadMW.fields([{ name: "thumbnail" }, { name: "image" }]), ProductController.create_product
);
module.exports = router;
Directory structure
My OS is windows, so I have included the config to replace the (:) from file names, but nothing seems to work for me. Any help to resolve the same is appreciated.
app.js file will be like this
// modules =================================================
var express = require('express');
var app = express();
const logger = require('morgan');
var bodyParser = require('body-parser');
const indexRouter = require("./routes/index");
const cors = require('cors');
const path = require("path");
config = require("./environments/index");
var http = require('http').Server(app);
// configuration ===========================================
var port = config.PORT || 8081; // set our port
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({
limit: '50mb',
extended: true,
parameterLimit: 50000
}));
app.use(cors());
app.use(logger('dev'))
app.use("/api", indexRouter);
app.use(express.static(path.join(__dirname, "/build")));
http.listen(port, () => {
console.log('Magic happens on port ' + port); // shoutout to the user
});
exports = module.exports = app;
create multerhelper.js file in your app nd add below code in it
const multer = require('multer');
// const fs = require('fs');
let fs = require('fs-extra');
let storage = multer.diskStorage({
destination: function (req, file, cb) {
let path = `/uploads`;
fs.mkdirsSync(path);
cb(null, __dirname + path);
},
filename: function (req, file, cb) {
// console.log(file);
cb(null, Date.now() + file.originalname.replace(/\s+/g, "-"));
}
})
var upload = multer({ storage: storage });
let createUserImage = upload.single('images');
let multerHelper = {
createUserImage,
}
module.exports = multerHelper;
In your product.js(route) file import this file
const multerhelper = require("../multerhelper.js");
router.post("/new",multerhelper.createUserImage,ProductController.
create_product);
Your directory structures look messed up.
Your uploads directory in fileUpload.js
is referring to a directory in ./api/middleware/uploads when it should be referencing ./utils/uploads (the paths relative to the module are incorrect).
// fileUpload.js
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.resolve(__dirname, `..${path.sep}..${path.sep}`, `${path.sep}utils${path.sep}uploads`);
},
__dirname returns the directory that the current file is in. So in this case it will be middleware. (I am deducing this based on the require in your Product.js file).
Try fixing the path to the uploads directory in the fileUpload.js by pointing at the same directory that the static files are being served from.
Also, I seen in the chat that Arya started with you that you are now managing to get it working on a *nix OS but not on windows.
Try changing your path separators to use path.sep from the path module. Arya's solution above looks good, i.e. the file paths now all look fixed relative to the static uploads directory. I have updated my answer to use path.sep based on the original code that you published and the directory structure that you provided.
Finally I was able to resolve this. I am posting the solution if anyone comes across the same issue (Windows OS to be specific), this will surely help.
I changed my directory structure by shifting the uploads folder directly outside instead of /utils/uploads(for the sake of easeness) & made a minor change in fileUpload.js:
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, process.cwd() + "/uploads/");
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname.replace(/\s+/g, "-"));
},
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage,
limits: {
fileSize: 1024 * 1024 * 5,
},
fileFilter: fileFilter,
});
module.exports = upload;
Instead of using __dirname, I replaced it with process.cwd(), and appended my target folder :
destination: function (req, file, cb) {
cb(null, process.cwd() + "/uploads/");
},
Reference: What's the difference between process.cwd() vs __dirname?
P.S. Thanks to #jeeves & #Arya for their thorough suggestions :)

how can I upload file in Node JS?

I have problem while trying to upload file.
I use mongo as database and reactjs for frontend.
file appears in the database but it does not appear in the project.
I wanna appear in the directory which I set in the code.
And this is backend API.
const express = require("express");
const fileRoutes = express.Router();
let File = require("../../models/File");
const multer = require("multer");
const fs = require("fs-extra");
const storageBase = require("../../config/keys").storageBase;
const isEmpty = require("is-empty");
const addDays = require("date-fns").addDays;
const moment = require("moment-timezone");
// const fileUpload = require("express-fileupload");
// app.use(fileUpload());
var storage = multer.diskStorage({
destination: function (req, file, cb) {
const { file_type, file_owner } = req.query;
const path =
`${storageBase}` +
`${file_type}/${moment(new Date()).tz("Asia/Shanghai").format("YYYY-MM/MM-DD")}/${file_owner}`;
if (!fs.existsSync(path)) {
fs.mkdirsSync(path);
}
cb(null, path);
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
var upload = multer({
storage: storage,
limits:{
files: 5,
fieldSize: 4096 * 1024 * 1024
}
});
fileRoutes
.route("/upload")
.post(upload.single("file_data"), function (req, res) {
const {
file_type,
file_description1,
file_description2,
file_description3,
file_owner,
file_owner_job,
file_register_date,
hour,
minute
} = req.query;
let file = new File({
file_type: file_type,
file_url: req.query.file_register_date + "/" + req.files.file_data.name,
file_description1: file_description1,
file_description2: file_description2,
file_description3: file_description3,
file_owner: file_owner,
file_owner_job: file_owner_job,
file_register_date: file_register_date + " " + hour + ":" + minute
});
file
.save()
.then(file => {
res.status(200).json({ file: "file uploaded successfully" });
})
.catch(err => {
res.status(400).send("upload failed");
});
});
module.exports = fileRoutes;
But file which I select for upload does not save in my project
If u know how to do pls help me.

req.body and req.file always return empty in multer

I'm following this Academind Tutorial for creating a node API endpoint for uploading images where I use body-parser for parsing json request and multer for parsing multipart/form-data request but every time I hit API using postman I get empty req.body and req.file too.
This is multer implementation looks like
const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
const checkAuth = require('../middleware/check-auth');
const _ = require('lodash');
const assert = require('chai').assert;
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/')
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString() + file.filename);
},
});
const fileFilter = (req, file, cb) => {
//reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype == 'image/png')
cb(null, true);
else cb(null, false);
}
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
// update profile image
router.post("/update_profile_image", upload.single('image'), (req, res, next) => {
console.log(req.file);
console.log(req.client_id)
res.send("Done")
});
module.exports = router;
and This is my app.js file implementation
app.use('/uploads', express.static('uploads'));
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
Thanks in advance

Resources