Multer does not save the file and I cannot see any logs... The folder is empty, no any files.
The image data is actually sent to the server as its console logged:
Screenshot of req.files from log
What am I doing wrong? Please help me find
const express = require('express');
const router = express.Router();
const path = require('path');
const multer = require('multer');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
console.log('destination') // can't see this log
cb(null, './uploads/');
},
filename: (req, file, cb) => {
console.log('filename') // can't see this log
const ext = file.mimetype.split("/")[1];
cb(null, Date.now() + path.extname(file.originalname));
}
});
const uploadImage = multer({
storage,
limits: { fieldSize: 1024 * 1024 },
fileFilter: (req, file, cb) => {
console.log('log!') // can't see this log
const ext = path.extname(file.originalname);
if (ext !== '.jpg' && ext !== '.jpeg' && ext !== '.png') {
console.log('not log!') // can't see this log
const err = new Error('Extention');
err.code = 'EXTENTION';
return cb(err);
}
console.log('log!') // can't see this log
cb(null, true);
}
}).single('photo');
router.post('/image', async (req, res) => {
console.log(req.files) // can see this log
uploadImage(req, res, err => {
console.log('upload') // can see this log
let error = '';
if (err) {
console.log(err)
if (err.code === 'LIMIT_FILE_SIZE') {
error = 'Max 500kb!';
}
if (err.code === 'EXTENTION') {
error = 'Only jpg and png!';
}
}
res.json({
ok: !error,
error
});
})
});
Multer finally gave an error, so the problem was resolved... The problem was that the names of these fields are different:
Server:
.single('pic');
And in the request to the server:
formData.append('pic', file);
The code seems to work fine for me but I just had to manually create the uploads directory.
I used postman to test this and the picture appeared in my uploads directory.
# Console Output
SERVER RUNNING ON 3000
undefined
log!
log!
destination
filename
upload
Related
I am trying to write an app using NodeJS and Express which will upload both a user avatar which is a single image (jpeg, png or webp) and 'legalDocs' which is essentially also a single file that can either be an image (jpeg, png or webp) or a pdf file. The avatar needs to be uploaded to ./resources/(userID)/userAvatar and the legal docs to ./resources/(userID)/legalDocs. Along with some other text info that will be posted along with the request as a multipart/form-data.
The userID is only generated after processing the text data posted.
Here's my current non-working code
APP.JS
//ENV
require("dotenv").config();
require("express-async-errors");
//EXPRESS
const express = require("express");
const server = express();
global.__basedir = __dirname;
//routes
const authRouter = require("./routes/authRoutes.js");
server.use(express.json());
server.use(express.urlencoded({extended: true}));
server.use(express.static("./public"));
server.use("/api/v0/auth", authRouter);
const port = process.env.PORT || 3000;
const start = async () => {
try {
server.listen(port, () => {
console.log(`--> Listening on port ${port}...`);
});
} catch (error) {
console.log(error);
console.log("Fatal error!");
}
}
start();
authRoutes.js:
const express = require("express");
const router = express.Router();
const {signupWithEmail} = require("../controllers/auth.js");
router.route("/signup").post(signupWithEmail);
module.exports = router;
../controllers/auth.js:
const multer = require("multer");
const processReq = require("../middleware/upload.js");
const signupWithEmail = async (req, res, next) => {
//Processing to get the ID happens here.
//For simplicity we can pretend like the ID is directly
//posted in a field with the name id.
const id = req.body.id;
console.log("id:");
console.log(id);
await processReq(id)(req, res, next);
res.status(200).send("done!");
}
upload.js
const multer = require("multer");
const errors = require("../errors");
const maxSize = 2 * 1024 * 1024;
const acceptableFileMIMETypes = ["image/jpeg", "image/png", "image/webp", "application/pdf"];
const userAvatarFilter = function (req, file, cb) {
if ((file.fieldname === "userAvatar" || file.fieldname === "legalDocs")
&& acceptableFileMIMETypes.includes(file.mimetype)
&& !(file.fieldname === "userAvatar" && file.mimetype === "application/pdf")) {
if (file.fieldname === "userAvatar") {
cb(null, true);
} else {
cb(null, false);
}
} else {
cb(new errors.BadRequestError("Both files are required, and must be JPEG, PNG, WEBP. Legal documents can also be PDF."));
}
}
const userlegalDocsFilter = function (req, file, cb) {
if ((file.fieldname === "userAvatar" || file.fieldname === "legalDocs")
&& acceptableFileMIMETypes.includes(file.mimetype)
&& !(file.fieldname === "userAvatar" && file.mimetype === "application/pdf")) {
if (file.fieldname === "userAvatar") {
cb(null, false);
} else {
cb(null, true);
}
} else {
cb(new errors.BadRequestError("Both files are required, and must be JPEG, PNG, WEBP. Legal documents can also be PDF."));
}
}
const uploadFunc = (id) => {
return (req, res, next) => {
const userAvatarStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + `/resources/${id}/userAvatar`);
},
filename: (req, file, cb) => {
console.log("Uploaded avatar!");
console.log(file.originalname);
console.log(file);
cb(null, file.originalname);
}
});
const legalDocsStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + `/resources/${id}/legalDocs`);
},
filename: (req, file, cb) => {
console.log("Uploaded legalDocs!");
console.log(file.originalname);
console.log(file);
cb(null, file.originalname);
}
});
const uploadAvatar = multer({
storage: userAvatarStorage,
limits: {
fileSize: maxSize,
files: 2
},
fileFilter: userAvatarFilter
}).any();
const uploadLegalDocs = multer({
storage: legalDocsStorage,
limits: {
fileSize: maxSize,
files: 2
},
fileFilter: userlegalDocsFilter
}).any();
uploadAvatar(req, res, next);
uploadLegalDocs(req, res, next);
next();
}
}
module.exports = uploadFunc;
The Problem
The first problem with my code is quite obvious, I cannot parse text data from the request unless it had been processed by multer (with the current code, console.logging the id shows that it's undefined), but I need the ID to be passed to multer in order to save the files to the correct directory. It seems like I need some way to process text only and generate the ID, then process the files.
The second problem is that, even if the ID is static - meaning that in ./controllers/auth.js the signupWithEmail function became:
const multer = require("multer");
const processReq = require("../middleware/upload.js");
const signupWithEmail = async (req, res, next) => {
const id = 1337;
console.log("id:");
console.log(id);
await processReq(id)(req, res, next);
res.status(200).send("done!");
}
I this is the output I get on the console when I try to post:
id:
1337
Uploaded legalDocs!
cours-analyse-numerique-ch1-21_22.pdf
{
fieldname: 'legalDocs',
originalname: 'cours-analyse-numerique-ch1-21_22.pdf',
encoding: '7bit',
mimetype: 'application/pdf'
}
node:_http_outgoing:771
throw new ERR_HTTP_HEADERS_SENT('remove');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client
at new NodeError (node:internal/errors:400:5)
at ServerResponse.removeHeader (node:_http_outgoing:771:11)
at write (C:\Users\der_u\source\repos\ATEEK\node_modules\finalhandler\index.js:282:9)
at AsyncResource.runInAsyncScope (node:async_hooks:204:9)
at listener (C:\Users\der_u\source\repos\ATEEK\node_modules\on-finished\index.js:170:15)
at onFinish (C:\Users\der_u\source\repos\ATEEK\node_modules\on-finished\index.js:101:5)
at callback (C:\Users\der_u\source\repos\ATEEK\node_modules\ee-first\index.js:55:10)
at IncomingMessage.onevent (C:\Users\der_u\source\repos\ATEEK\node_modules\ee-first\index.js:93:5)
at IncomingMessage.emit (node:events:513:28)
at endReadableNT (node:internal/streams/readable:1359:12) {
code: 'ERR_HTTP_HEADERS_SENT'
}
and only 'legalDocs' get uploaded.
Is there any way to solve these problems without using a temporary directory to save all the files then move them once the ID had been generated? And what does the [ERR_HTTP_HEADERS_SENT] error mean?
I'm trying to create a form where it will be a field to upload multiple images, like real estate site.
In Postman, I set the body to form-data and try to post two files to http://localhost:8080/api/files/upload while server is running to check if it is working before implementing the view in React, but getting this error:
Also addition information, I'm using
app.use("/api/files", imageRoutes); // => /api/files/
RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: LIMIT_UNEXPECTED_FILE
at new NodeError (node:internal/errors:393:5)
at ServerResponse.writeHead (node:_http_server:343:11)
at ServerResponse._implicitHeader (node:_http_server:334:8)
at write_ (node:_http_outgoing:867:9)
at ServerResponse.end (node:_http_outgoing:977:5)
at ServerResponse.send (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\response.js:232:10)
at ServerResponse.json (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\response.js:278:15)
at C:\Users\User\Downloads\yedy-react-backend-master\server.js:35:7
at Layer.handle_error (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\router\layer.js:71:5)
at trim_prefix (C:\Users\User\Downloads\yedy-react-backend-master\node_modules\express\lib\router\index.js:326:13)
images-controllers.js
const fs = require("fs")
const uploadImage = (req, res, next) => {
const files = req.files;
if (! files) {
res.status(400).send('Please choose files');
return; }
// convert images into base64 encoding
let imgArray = files.map((file) => {
let img = fs.readFileSync(file.path)
return encode_image = img.toString('base64')
})
let result = imgArray.map((src, index) => { // create object to store data in the collection
let finalImg = {
filename: files[index].originalname,
contentType: files[index].mimetype,
imageBase64: src
}
let newUpload = new UploadModel(finalImg);
return newUpload.save().then(() => {
return {msg: `${
files[index].originalname
} Uploaded Successfully...!`}
}).catch(error => {
if (error) {
if (error.name === 'MongoError' && error.code === 11000) {
return Promise.reject({error: `Duplicate ${
files[index].originalname
}. File Already exists! `});
}
return Promise.reject({
error: error.message || `Cannot Upload ${
files[index].originalname
} Something Missing!`
})
}
})
});
Promise.all(result).then(msg => { // res.json(msg);
res.redirect('/')
}).catch(err => {
res.json(err);
})
}
exports.uploadImage = uploadImage;
multer.js
const multer = require('multer');
// set storage
var storage = multer.diskStorage({
destination : function ( req , file , cb ){
cb(null, 'uploads')
},
filename : function (req, file , cb){
// image.jpg
var ext = file.originalname.substr(file.originalname.lastIndexOf('.'));
cb(null, file.fieldname + '-' + Date.now() + ext)
}
})
module.exports = store = multer({ storage : storage })
image-routes.js
const express = require("express");
const router = express.Router()
const store = require("../middleware/multer")
const imagesController = require("../controllers/images-controllers");
router.post('/upload', store.array('images', 12), imagesController.uploadImage)
module.exports = router;
It looks like you are not sending images properly from the Postman. You should specify the name of images array to be images because you specified that in the Multer middleware.
You should send it like this:
Not able to upload image in MongoDB database using NodeJS
Middleware fieldName and key name is also same still not working. no solution on internet
server.js
import cookieParser from "cookie-parser";
import multer from "multer";
export const app = express();
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
let upload = multer()
app.use(upload.array())
app.use("/",routLog,router)
app.listen(process.env.PORT,()=>{
db()
console.log(`Server is Runing on http://localhost:${process.env.PORT}`)
})
blogroute.js
import { fileUploder } from "../middelware/fileUploder.js";
export const blogRouter = Router()
blogRouter.post('/:user/create-post', fileUploder.single("file"), blogsController.createPos
This middleware file fileUplad.js which is use to store in upload folder and then from that will store in MongoDB in buffer ferment
import { randomBytes } from "crypto";
import multer from "multer";
import path from "path";
const storage = multer.diskStorage({
destination: './upload',
filename: async (req, file, callback) => {
return await randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "upload"
};
resolve(fileInfo);
})
}
})
export const fileUploder = multer({
storage: storage,
limits: { fileSize: 2000 },
fileFilter: (req, file, callback) => {
allowedFileTypes = /jpeg|jpg|png|gif/;
if (file.mimetype == 'jpg' || file.mimetype == 'jpeg' || file.mimetype == 'png', file.mimetype == 'gif') {
return cb(null, true);
} else {
cb("Invalid file type. Only JPEG, PNG and GIF file are allowed.")
}
}
})
MulterError: Unexpected field
at wrappedFileFilter (/mnt/ssd/BlogProject/backendapi/node_modules/multer/index.js:40:19)
at Multipart.<anonymous> (/mnt/ssd/BlogProject/backendapi/node_modules/multer/lib/make-middleware.js:107:7)
at Multipart.emit (node:events:527:28)
at HeaderParser.cb (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:358:14)
at HeaderParser.push (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:162:20)
at SBMH.ssCb [as _cb] (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:394:37)
at feed (/mnt/ssd/BlogProject/backendapi/node_modules/streamsearch/lib/sbmh.js:248:10)
at SBMH.push (/mnt/ssd/BlogProject/backendapi/node_modules/streamsearch/lib/sbmh.js:104:16)
at Multipart._write (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:567:19)
at writeOrBuffer (node:internal/streams/writable:389:12)```
This is simple example :
let multer = require('multer');
let multerImage = multer().single('file');
function uploadAvatar(req, res) {
let phone = data.phoneNumber;
multerImage(req, res, () => {
let file = req.file;
if (isUndefined(file))
return Json.builder(Response.HTTP_BAD_REQUEST);
let {
fileUrl
} = File.validationAndWriteFile(file.buffer, Util.getFileFormat(file.originalname));
Update.img(phone, fileUrl, result => {
if (!result)
return Json.builder(Response.HTTP_BAD_REQUEST);
Json.builder(Response.HTTP_OK);
});
});
}
I am using Multer to save images but I need to get the path of the image to save it to MongoDB. I am trying to get the path with req.file but it always tells me on the console that it is undefined.
this is my route:
import { Router } from 'express';
import { check, validationResult } from 'express-validator';
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/products')
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString().replace(/:/g, '-') + file.originalname)
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === 'image/jpeg' ||file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(null, false);
//cb(new Error('I don\'t have a clue!'))
}
}
const upload = multer(
{ storage: storage,
limits:{
fileSize: 1024 * 1024
},
fileFilter: fileFilter
});
let router = Router();
router.post('/', upload.single('img'),
newProduct
);
And in the new Product controller I am trying to read the req.file but the console tells me that it is undefined:
Controller:
import { Product } from '../models'
let newProduct = async (req, res = response ) => {
console.log('file ' + req.file); //UNDEFINED
try {
let { status, user, ...body } = req.body;
let productDB = await Product.findOne ( { 'name': body.name } );
if (productDB) {
return res.status(400).json({
msg:`El producto ${ productDB.name } ya existe`
})
}
let data = {
...body,
name: body.name,
user: req.user._id
}
let product = new Product( data );
await product.save();
res.status(200).json( product );
} catch (error) {
return res.status(400).json({
error
});
}
}
Console:
Thanks for your help.
you can try to do this in filename instead:
filename: function (req, file, cb) {
req.imageName = new Date().toISOString().replace(/:/g, '-') + file.originalname
cb(null, req.imageName)
}
then there:
console.log('file ' + req.file); //UNDEFINED
//you can get imageName instead
console.log('imageName',req.imageName)
//if you want url to store in database you can do this
//supposing your have images directory in root of your node server
const url = `${req.protocol}://${req.get('host')}/images/${req.body.image}`
I am working on a web application for an online library. I want to extract metadata from the PDF's that will be uploaded and for that I am using the nodejs library pdf.js-extract and multer-gridfs-storage for the upload. The problem is that I am receiving a PDF file (req.file) and the function requires a path or link to the PDF file and therefore shows the error
"TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be one of type string, Buffer, or URL. Received type object"
I would like to know if there is a way to pass a file as a link, save the file locally temporarily or find another library that fits my needs.
This is my current code.
const PDFExtract = require('pdf.js-extract').PDFExtract;
app.post('/upload', upload.single('file'), (req, res) => {
const pdfExtract = new PDFExtract();
const options = {};
pdfExtract.extract(req.file, options, (err, data) => {
if (err){
res.status(404).send({ message: err });
}
res.status(200).send({ message: data });
});
});
(Edit for clarification) I am using multer with gridFS to upload a file to mongoose.
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
Solution inspired by Oliver Nybo
app.post('/upload', upload.single('file'), (req, res) => {
const pdfExtract = new PDFExtract();
const options = {};
var readableStream = gfs.createReadStream({ filename : req.file.filename });
var buff;
var bufferArray = [];
readableStream.on('data',function(chunk){
bufferArray.push(chunk);
});
readableStream.on('end',function(){
var buffer = Buffer.concat(bufferArray);
buff=buffer;
pdfExtract.extractBuffer(buff, options, (err, data) => {
if (err) {
res.status(404).send({ message: err });
}
res.status(200).send({ message: data });
});
})
});
According to multer's api documentation, you can use req.file.path to get the full path of the uploaded file.
const PDFExtract = require('pdf.js-extract').PDFExtract;
app.post('/upload', upload.single('file'), (req, res) => {
const pdfExtract = new PDFExtract();
const options = {};
pdfExtract.extract(req.file.path, options, (err, data) => {
if (err){
res.status(404).send({ message: err });
}
res.status(200).send({ message: data });
});
});
Edit: I just read the multer options and there is an option called preservePath.
preservePath - Keep the full path of files instead of just the base name
Edit 2: I think you need to extract the file from the database with gridfs-stream, then convert it into a buffer (like in this thread), and then use PDFExtract's extractBuffer function.