Multer is not identifying the multipart/form-data request file - node.js

Client (ReactJS/Axios):
const handleSubmit = async (croppedImage: any) => {
var formData = new FormData();
formData.append('file', croppedImage);
formData.append('name', croppedImage.name)
api.post(
'/firebase',
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
}
}
)
.then(() => console.log('Success'))
.catch(e => console.error(e))
}
Multer middleware:
const { v4: uuidv4 } = require('uuid');
const multer = require('multer');
const error = new Error('Only .png format allowed!');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
if (!file) return cb(error);
cb(null, DIR);
},
filename: (req, file, cb) => {
if (!file) return cb(error);
const fileName = file.originalname.toLowerCase().split(' ').join('-');
cb(null, uuidv4(4) + '-' + fileName)
}
});
var upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (!file) return cb(error);
if (file.mimetype == "image/png") {
cb(null, true);
} else {
cb(null, false);
return cb(error);
}
}
});
module.exports = upload.single('file');
Next():
async (req, res) => {
const fileName = req.file.filename;
Here req.file is
undefined!
...
}
When I try to pass a image using Axios, req.file appears as undefined in the controller.
I think the error is when passing the multipart/form-data, because when I do it through Insonmia Rest it works!

Form Example :
<form action="/insert" enctype="multipart/form-data" method="post">
<div class="form-group">
<input type="file" class="form-control-file" name="uploaded_file">
<input type="submit" value="Get me the stats!" class="btn btn-default">
</div>
</form>
Save and get file
if (req.files)
{
let uploaded_file = req.files[0].filename;
console.log(uploaded_file);
}

croppedImage was like a blob url or something like that, so:
Client (ReactJS/Axios):
let blob = await fetch(croppedImage).then(r => r.blob());
var formData = new FormData();
formData.append('file', blob, 'file.jpeg')

Related

File upload with react, nodeJS multer doesn't work

i made this function, which takes the file value ( image ) from the file state and send it to upload route, through FormData object
const [file, setFile] = useState(null);
const submitPost = async (e) => {
...
e.preventDefault();
if (file) {
const data = new FormData();
const fileName = `${Date.now()}${file.name}`;
data.append("name", fileName);
data.append("file", file);
try {
await fetch("http://localhost:8000/upload", {
headers: {
"Content-type": "application/json",
},
method: "POST",
body: JSON.stringify(data),
});
// window.location.reload();
} catch (err) {
console.log(err);
}
}
};
the file value in the file state is coming from form file input
<form className="postOptions" onSubmit={submitPost}>
<div className="postAreaBot">
<label htmlFor="file" className="downloadImg">
<AddAPhotoIcon className="postIcon" />
<span>Image</span>
</label>
<input
type="file"
accept=".png,.jpeg,.jpg"
onChange={(e) => setFile(e.target.files[0])}
id="file"
style={{ display: "none" }}
></input>
</div>
<button type="submit">Post</button>
</form>
and then sending the file value to this route for upload on folder images inside folder public using multer and path
app.use("/images", express.static(path.join(__dirname, "public/images")));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/images");
},
filename: (req, file, cb) => {
cb(null, req.body.name);
},
});
const upload = multer({ storage });
app.post("/upload", upload.single("file"), (req, res) => {
try {
return res.status(200).json("File uploaded successfully");
} catch (err) {
console.log(err);
}
});
but the image is not being uploaded
what have i tried
i tested the route using file.originalName for postman instead of file.req.body and the route does indeed works and images are being uploaded successfully, i also checked the values name and file in the data object and it appends it successfully, i can't see what is the problem, why it doesn't upload the image file through the react fetch request?
Just remove the JSON.stringify. And change your content type, like the example below:
await fetch("http://localhost:8000/upload", {
headers: {
"Content-type": "multipart/form-data",
},
method: "POST",
body: data,
});

Not getting image file in nodejs after posting from react

I'm sending the image file from react to nodejs. I used the SetImage function where the image file selected by the user is set to the 'myimage' state variable and from handleSubmit I'm posting the images to the '/blog/posts' endpoint using Axios. the image file is getting loaded on the 'myimage' state variable but as I posted using the Axios, I can't see all the data of the image file and giving me the empty object.
This is my React code:-
import React, { useState, useRef } from "react";
import './postblog.css';
import axios from '../axios';
const PostBlog = () => {
const [myimage,setImage] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
axios.post('/blog/posts', {
image:myimage
}).then((res) => {
console.log(res.body);
console.log('successfully posted');
});
}
const SetImage = (e)=>{
console.log(e.target.files[0]);
setImage(e.target.files[0]);
console.log(myimage);
}
return (
<div className="postblog">
<form onSubmit={handleSubmit} enctype="multipart/form-data">
<input type="file" placeholder="Choose your file" onChange={(e)=>{SetImage(e)}} name="myImage"/>
</div>
<button type="submit">Post</button>
</form>
</div>
);
}
export default PostBlog;
This is my Nodejs code:-
var storage = multer.diskStorage({
destination: function(req, res, cb) {
cb(null, './Routers/blog/uploads');
},
filename: function(req, file, cb) {
cb(null, Date.now() + file.originalname);
}
});
var upload = multer({
storage: storage,
limits: {
fieldsize: 1024 * 1024 * 3
}
});
blog.post('/posts', upload.single('myimage'), (req, res, next) => {
console.log(req.body);
console.log(details);
})
The output in the console I'm getting is an empty object
You should send file with content type as multipart/form-data but in your code you are sending file as application/json content type.
...
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('file', myimage);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
};
axios.post('/blog/posts', formData, config).then((res) => {
console.log(res.body);
console.log('successfully posted');
});
}
...

multer. How can I skip a file if it is on disk?

How can I skip a file using multer upload if it is already on disk? It re-downloads and updates when I use the following code
attachments.component.js
import React, { Component } from 'react';
import axios from 'axios';
export default class FilesUploadComponent extends Component {
constructor(props) {
super(props);
this.onFileChange = this.onFileChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
imgCollection: ''
}
}
onFileChange(e) {
this.setState({ imgCollection: e.target.files })
}
onSubmit(e) {
e.preventDefault()
let formData = new FormData();
for (const key of Object.keys(this.state.imgCollection)) {
formData.append('imgCollection', this.state.imgCollection[key])
}
axios.post("/api/attachments/upload", formData, {
}).then(() => {}).catch(() => {})
}
render() {
return (
<div className="container">
<div className="row">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<input type="text" name="FIO" />
</div>
<div className="form-group">
<input type="file" name="imgCollection" onChange={this.onFileChange} multiple />
</div>
<div className="form-group">
<button className="btn btn-primary" type="submit">Upload</button>
</div>
</form>
</div>
</div>
)
}
}
attachments.router.js
let express = require('express'),
multer = require('multer'),
router = express.Router(),
Attachment = require('../models/Attachment');
const DIR = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
const fileName = file.originalname;
cb(null, fileName)
}
});
const upload = multer({
storage: storage
});
router.post('/upload', upload.array('imgCollection'), async (req, res) => {
try{
const reqFiles = [];
for (let i = 0; i < req.files.length; i++) {
const file_name = 'public/' +req.files[i].filename
reqFiles.push(file_name)
const find = await Attachment.findOne({ file_name: file_name })
if(find){
return res.status(400).json( { message: 'File ' + file_name + ' already save' } )
}
}
console.log(reqFiles)
reqFiles.map(async name => {
const attachment = new Attachment({
file_name: name
})
await attachment.save()
})
res.status(201).json({ message: 'Files saved' })
}catch (e) {
console.log(e.message)
}
})
module.exports = router;
Attachment.model.js
const {Schema, model} = require('mongoose')
const schema = new Schema({
file_name: {
type: String, required: true, unique: true
}
},
{
timestamps: true
})
module.exports = model('Attachment', schema)
Ideally, I would like to first perform some operations in mongodb, then give the correct answer to the user, but to get the necessary data, I first save the files using multer. Tell me what I'm doing wrong
Make use of the fileFilter function. Here's a full example:
const express = require('express')
const multer = require('multer')
const fs = require('fs')
const path = require('path')
const app = express()
const port = 3000
const UPLOAD_DIR = '/tmp/'
const fileFilter = (req, file, cb) => {
if (fs.existsSync(path.join(UPLOAD_DIR, file.originalname))) {
console.log('skipped')
cb(null, false)
return
}
cb(null, true)
}
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, UPLOAD_DIR)
},
filename: function (req, file, cb) {
const fileName = file.originalname;
cb(null, fileName)
}
})
const upload = multer({
fileFilter,
storage,
});
app.post('/', upload.single('avatar'), (req, res) => {
res.send('Uploaded!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})

Upload a profile pic with Reactjs and Nodejs to MongoDb

I am trying to let the user update their profile with their own avatar but for some reason, it doesn't work. I am sending a FormData from the front-end and catch it with nodeJs before storing it to MongoDB. user has 3 options to update: name, about, avatar. Whenever I try to update the name and about it works just fine but when I am uploading an avatar I get a 500 error. Can you guys take a look? FYI I am a begginer.
Here is my code:
FRONT-END
const handleFileInputChange = (e) => {
const file = e.target.files[0];
previewFile(file);
setUser({
...user,
avatar: file,
});
};
const onSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('name', user.name);
formData.append('about', user.about);
formData.append('avatar', user.avatar);
editUserProfile(formData); // external axios.patch f.
};
const editUserProfile = async (formData) => {
try {
await axios.patch('api/v1/users/me', formData, {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
dispatch({
type: EDIT_USER,
});
} catch (err) {
console.log(err);
}
};
<form onSubmit={onSubmit} encType="multipart/form-data">
<input
// accept="image/*"
accept=".png, .jpg, .jpeg"
type="file"
name="avatar"
// value={fileInputState}
onChange={handleFileInputChange}
style={{ display: 'none' }}
id="icon-button-file"
/>
...
BACK-END
router.patch('api/v1/users/me', protect, upload.single('avatar'), getMe, editUser = async (req, res) => {
try {
const { name, about } = req.body;
const profileFileds = {};
if (name) profileFileds.name = name;
if (about) profileFileds.about = about;
if (req.file) profileFileds.avatar = req.file.filename;
const user = await User.findByIdAndUpdate(req.params.id, profileFileds, {
new: true,
runValidators: true,
});
if (!user) {
return next(
new custom error...
);
}
res.status(200).json({
status: 'success',
data: {
user,
},
});
} catch (err) {
console.log(err) error 400 ...
}
}););
Sorry guys if it's a long code and I really appreciate it, I am struggling 2 days now and can't figure it out
//MULTER CONFIG just in case but it shouldn't be a problem with it
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/images');
},
filename: function (req, file, cb) {
cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname));
},
});
const fileFilter = (req, file, cb) => {
const allowedFileTypes = ['image/jpeg', 'image/jpg', 'image/png'];
if (allowedFileTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 2,
},
fileFilter: fileFilter,
});

How to upload multiple file with react/node and multer

I'm trying to upload multiple files with React/express and multer. But can't find what's wrong in my code...(I tried many solutions that I found here but I can't see where I'm wrong).
Here is my code :
**Front : **
function App() {
const [file, setFile] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
let newArr = [];
for (let i = 0; i < file.length; i++) {
newArr.push(file[i]);
}
formData.append('monfichier', newArr);
console.log(formData.get('monfichier'));
axios
.post('http://localhost:3000/uploaddufichier', formData)
.then((res) => res.data);
};
return (
<div className='App'>
<form
onSubmit={handleSubmit}
method='POST'
encType='multipart/form-data'
action='uploaddufichier'
>
<input
type='file'
name='monfichier'
onChange={(e) => setFile(e.target.files)}
multiple
/>
<button> envoyer </button>
</form>
</div>
enter code here
BACK
const multer = require('multer');
const fs = require('fs');
const cors = require('cors');
const path = require('path');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'file-storage');
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now());
},
});
const upload = multer({ storage: storage });
app.use(express.json());
app.use(router);
app.use(cors());
app.use('/file-storage', express.static('file-storage'));
app.use(function (err, req, res, next) {
console.log('This is the invalid field ->', err.field);
next(err);
});
app.post(
'/uploaddufichier',
upload.array('monfichier'),
function (req, res, next) {
console.log(req.files);
fs.rename(
req.files.path,
'file-storage/' + req.files.originalname,
function (err) {
if (err) {
res.send('problème durant le déplacement');
} else {
res.send('Fichier uploadé avec succès');
}
}
);
}
);
For now the back-end console.log(req.files) return an empty array...
And the front-end console.log(formData.get('monfichier') return [object File], [object File]
IF anyone could help me for that issue....It'll be glad :)
A small tweak would have fixed it let me fix it and highlight the tweak that will fix it below.
function App() {
const [file, setFile] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
let newArr = [];
//********* HERE IS THE CHANGE ***********
for (let i = 0; i < file.length; i++) {
formData.append('monfichier', file[i]);
}
console.log(formData.get('monfichier'));
axios
.post('http://localhost:3000/uploaddufichier', formData)
.then((res) => res.data);
};
return (
<div className='App'>
<form
onSubmit={handleSubmit}
method='POST'
encType='multipart/form-data'
action='uploaddufichier'
>
<input
type='file'
name='monfichier'
onChange={(e) => setFile(e.target.files)}
multiple
/>
<button> envoyer </button>
</form>
</div>
The array method on multer accepts multiple files over the wire provided they have the corresponding name you specified (in our case 'monfichier'). So what we have done with the for-loop on the front-end is append several files with the same name - monfichier.
This question has been unanswered for 9months but hopefully, it will be helpful to you or anyother person that is facing this blocker.
#cheers
I know this is an old question, however, this solution will be useful to anyone who is experiencing a similar challenge.
The solution is simple. You must attach your photos with the same name on the frontend. then sending them to the backend. The rest will be handled by Multer, and you can access your files via 'req.files'.
Example
React
const formData = new FormData();
for (let i = 0; i < event.target.files.length; i++) {
formData.append("images", event.target.files[i]);
}
fetch("http://localhost:3003/api/v1/upload", {
method: "POST",
body: formData,
});
};
Backend - ExpressJs + Multer
gallaryRouter
.route("/:galleryId")
.post(
UploadGallery.array("images"),
(req,res)=>{console.log(req.files)}
)

Resources