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)}
)
Related
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')
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}`)
})
I'm trying to create a small social network in which we can send posts (with or without images).
I manage to create posts without an image (just text), it works very well, but as soon as I add an image to my form and submit the form, it is impossible to find the image. Normally it should be saved in the "images" folder but it is always empty.
I am using multer to do that, here is my code :
My form component :
const WhatsUpForm = ({ className, id, name, placeholder }) => {
const [inputValue, setInputValue] = useState("");
const inputHandler = (e) => {
setInputValue(e.target.value);
};
const submitHandler = async (e) => {
e.preventDefault();
const post = {
author_firstname: JSON.parse(localStorage.getItem("user")).user_firstname,
author_lastname: JSON.parse(localStorage.getItem("user")).user_lastname,
message: inputValue,
date_creation: dayjs().format(),
image_url: ""
};
// POST request
await POST(ENDPOINTS.CREATE_POST, post);
// document.location.reload()
};
return (
<form className={className} onSubmit={submitHandler} method="POST" action="/api/post" enctype="multipart/form-data">
<input className="testt" type="text" id={id} name={name} placeholder={placeholder} required value={inputValue} onChange={inputHandler}/>
<div className="icons_container">
<input type="file" name="image" id="image" className="icons_container__add_file" />
<label for="image">
<FontAwesomeIcon icon={faImages} />
</label>
<button type="submit" className="icons_container__submit">
<FontAwesomeIcon icon={faPaperPlane} />
</button>
</div>
</form>
);
};
My routes and the multer's code :
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "../images");
},
filename: (req, file, callback) => {
console.log("multer");
console.log("file :", file);
callback(null, Date.now() + path.extname(file.originalname));
},
});
const upload = multer({ storage: storage });
// Post CRUD
router.get("/", auth, postCtrl.getAllPosts);
router.post("/", auth, upload.single("image"), postCtrl.createPost);
router.delete("/:id", auth, postCtrl.deletePost);
router.put("/:id", auth, postCtrl.updatePost);
console.log("multer") is not trigger, and when i look the payload in network tab in my browser, i don't see any images.
And finally, my controller for createPost function :
exports.createPost = (req, res, next) => {
let { body } = req;
delete(req.body.image_url)
body = {
...body,
likes: "",
};
const sql = "INSERT INTO posts SET ?";
db.query(sql, body, (err, result) => {
if (err) {
res.status(404).json({ err });
throw err;
}
res.status(200).json({ msg: "Post added..." });
});
};
For now, i don't want to put the image's URL in my SQL DB, i just want to save the image in my images folders. I have verified the path (../images) and it's coorect.
How do I save the image in my image folder?
I don't see the file data gets sent to server from your POST request.
// object post doesn't have the file data
await POST(ENDPOINTS.CREATE_POST, post);
Consider using FormData
const submitHandler = async (e) => {
e.preventDefault();
const post = new FormData();
// non form data
formData.append("author_firstname", JSON.parse(localStorage.getItem("user")).user_firstname);
...
// form data
formData.append("image", document.getElementById("image").files[0]);
...
// POST request
await POST(ENDPOINTS.CREATE_POST, post);
// document.location.reload()
};
I am trying to add the ability to upload an image and then update a user object with that image path. When I upload the image using Insomnia client and send the request, I am able to successfully add the image and path to MongoDB. NodeJS user route below:
const express = require ("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const Users = require("../models/users");
const auth = require("../middleware/auth");
const multer = require('multer');
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/');
},
filename: function(req, file, cb){
cb(null, file.originalname);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png' || file.mimetype == "image/jpg") {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
}
};
const upload = multer({storage: storage, limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
// REQUEST TO FIND USER BY ID AND ADD A PROFILE PICTURE
router.put('/update/:id', upload.single('userImage'), (req, res) => {
Users.findById(req.params.id)
.then(user => {
user.userImage = req.file.path || user.userImage,
user
.save()
.then(() => res.json("The User is UPDATED succesfully!"))
.catch(err => res.status(400).json(`Error: ${err}`));
})
.catch(err => res.status(500).json(`Error: ${err}`));
console.log(req.body);
console.log(req.file);
});
However, when I test the code on the client I receive the following 500 error: ""Error: TypeError: Cannot read property 'path' of undefined." I am using React Hooks and Axios, code below:
import React, { useState, useContext } from 'react';
import Exit from '../cancel.svg';
import Modal from 'react-modal';
import { useForm } from "react-hook-form";
import axios from 'axios';
import UserContext from "../context/UserContext";
import { useHistory } from "react-router-dom";
Modal.setAppElement('#root');
const ProfilePicture = () => {
const [modalIsOpen, setModalIsOpen]= useState (true);
const { register, handleSubmit } = useForm ();
const [userImage, setUserImage] = useState();
const onSubmit = (data) => console.log(data);
const { userData } = useContext(UserContext);
const history = useHistory();
const changeOnClick = e => {
const users = {
userImage
};
console.log(users);
axios
.put(`http://localhost:5000/users/update/${userData.user.id}`, users)
.then(res => console.log(res.body))
.catch(err => {
console.log(err);
});
history.push("/Landing")
}
return(
<Modal isOpen={modalIsOpen} onRequestClose={() => setModalIsOpen(false)} className='finishBorder'>
<div className='border-bottom-two'>
<img onClick= {() => setModalIsOpen(false)} className='newexitButton'src={Exit} alt='X' />
<span className='lastThing'>One last thing! Add a profile picture</span>
</div>
<form onSubmit={handleSubmit(changeOnClick)} encType="multipart/form-data" >
<div>
<span className="dot"></span>
<input onChange={e => setUserImage(e.target.value)} ref = {register} type='file' name='userImage' className='picUploader'/>
{/* <button>submit</button> */}
</div>
<div>
<button type='submit' className='doneButton'>Done</button>
<div>
<span className='laterTwo'>I'll do this later</span>
</div>
</div>
</form>
</Modal>
)
}
export default ProfilePicture;
Appreciate the help!
What you are sending from the client is a "fakepath", not a file. To access the file from the input use:
setUserImage(e.target.files[0])
Also, I think multer only works with multipart/form-data, so you should check that, but is as easy as:
const formData = new FormData();
formData.append([image name], [image file]);
axios.post(`http://localhost:5000/users/update/${userData.user.id}`, formData, {
headers: { "Content-Type": "multipart/form-data" }})
I am creating a blog so, wanted to upload an image for each post. I used express-file upload for this purpose. Using nodejs I have done the following to save the image sent from the client-side in MongoDB. When I print the value of req.files in the console I get undefined.
exports.addPost = (req, res) => {
const file = req.files.file
const post = new Blog()
post.title = req.body.title
post.des = req.body.des
post.file = file
post.save((err, doc) => {
if (!err) {
res.send(doc)
} else {
console.log(err)
}
})
}
In react I have Addpost.js that sets the state and handles the form submit as follows:
const Addpost=()=> {
const [title, settitle] = useState('')
const [des, setdes] = useState('')
const [file, setfile] = useState('');
const {addPost}=useContext(Globalcontext)
const handleSubmit = (e)=>{
e.preventDefault()
const formData = new FormData()
formData.append('file',file)
const addedValue={
title,
des,
formData
}
addPost(addedValue)
settitle('')
setdes('')
setfile('')
}
const onChange=(e)=>{
const file=e.target.files[0]
setfile(file)
}
return (
<div>
<form onSubmit={handleSubmit} encType="multipart/form-data">
<input type="text" name="title" value={title} onChange={(e)=>settitle(e.target.value)}/>
<input type="text" name="des"value={des} onChange={(e)=>setdes(e.target.value)}/>
<input type="file" name="file" onChange={onChange}/>
<button type='submit' value='submit'>Add Post</button>
</form>
</div>
)
}
The AXIOS post request is sent as:
function addPost(postdetail) {
axios.post('http://localhost:4000/blog', postdetail).then(res => {
dispatch({
type: 'ADD_DATA',
payload: res.data
})
}).catch(error => {
console.log(error)
})
}
I am getting the error:
Cannot read property 'file' of undefined
1. Probably you didn't register middleware.
According to the doc example, you should register express-fileupload middleware before you refer req.files:
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
// default options
app.use(fileUpload());
Also don't forget to add null check in case when no files are uploaded:
app.post('/upload', function(req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
let file = req.files.file;
// do something with uploaded temp file
}
2. Content type should be multipart/form-data when you upload file
const handleSubmit=(e)=>{
e.preventDefault()
const formData=new FormData()
formData.append('file', file)
setfile('')
}
function addPost(postdetail){
axios.post('http://localhost:4000/blog',formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res=>{
dispatch({
type:'ADD_DATA',
payload:res.data
})
}).catch(error=>{
console.log(error)
})
}
3. Other form fields(des, title) may not be submitted using multipart/formdata
Consider open two routes for blog creation.
[POST] '/blog/upload-image' for image upload
[POST] '/blog/new for create blog (title, des and image_id acquired from image upload response)