Image Upload from React to Express server with Multer - node.js

I get undefined when I try to read the req.files and if I read req.body I get file: [object Object] which, when read on the front end before being sent is
Icon: File(4076381)
lastModified: 1513292207750
lastModifiedDate: Thu Dec 14 2017 17:56:47 GMT-0500 (EST) {}
name: "image.jpeg"
size: 4076381
type: "image/jpeg"
webkitRelativePath: ""
but after
let upload = new FormData();
upload.append('file', user);
console.log(upload);
upload logs FormData = {} so I have no idea why .append() did not work. user is the Icon object shown above.
but when sent back it is the same [object Object] as on the server of course.
I am trying to understand why req.file is not readable or any other alternative for how to read the uploaded file so that I can then do whatever I want with it (end game, store in a db as a field in mongodb, then pull out and render on the client side after logging in).
Relevant code
Register.js
// standard React stuff
handleUpload = (e) => {
console.log(e.target.files[0]);
this.setState({ Icon: e.target.files[0] });
};
handleSubmit = async (e) => {
e.preventDefault();
const dispatchObject = {
Icon: this.state.Icon
}
await this.dispatchSubmit(dispatchObject);
}
render(){
return (
<form noValidate autoComplete="off" onSubmit={this.handleSubmit}>
/>
<Input
id='Icon'
type='file'
name='Icon'
accept='image/png, image/jpeg'
onChange={this.handleUpload}
/>
<input type='submit' value='Submit'/>
</form>
)
}
action/index.js
export const register = (user, history) => {
console.log(user);
let upload = new FormData();
upload.append('file', user);
return dispatch => {
axios.post('http://localhost:9000/register', upload)
.then((res) => {
console.log(res.data);
console.log('User successfully created');
})
.catch(err => console.log(err) );
}
}
server.js
import multer from 'multer';
const storage = multer.diskStorage({
destination: './files',
filename(req, file, cb) {
console.log(file);
cb(null, `${new Date()}-${file.originalname}`);
},
});
const upload = multer({ storage });
app.post('/register', upload.single('file'), async (req, res) => {
console.log(req.body.file);
res.json(req.body.file);
});
Multer will create the relevant folder called ./file but there is never anything in it and req.file is not accessible. I cannnot send it through the body in a standard manner because it comes out in the [object Object] format.

you are logging req.body.file but it is req.file.

The solution was too change the below to user.Icon.
export const register = (user, history) => {
console.log(user);
let upload = new FormData();
upload.append('file', user);
I also separated any other files that I wanted to send in another api call.

Related

Why multer is not uploading my image in the public folder?

I am creating one feature to upload images using multer . But i am not able to do so . A link is being created in the prescription but the image is not being saved in the public/images folder .
This is the jsx code.
<p>Upload Prescription</p>
{selectedImage && (
<div>
<img alt="not found" width={"250px"} src={URL.createObjectURL(selectedImage)} />
<br/>
</div>
)}
<br />
<br />
<input
type="file"
name="image"
onChange={(event) => {
console.log('hi');
console.log(event.target.files[0]);
setSelectedImage(event.target.files[0]);
}}
/>
<button onClick={(e)=>{setSelectedImage(null)}}>
Remove
</button>
</div>
</div>
<button onClick={handleClick}>
Add Record
</button>
This is the handleClick function
const handleClick = async (e) =>{
e.preventDefault();
console.log(URL.createObjectURL(selectedImage));
const recordData =
{
diseasename,weight,height,medicines,desc,checkdate,patientId, prescription:URL.createObjectURL(selectedImage)
}
try{
const res = await axios({
method: "POST",
url: "http://localhost:3000/api/record/createrecord",
data: recordData,
withCredentials: false
});
console.log(res.data);
}
catch(err){
console.log(err);
}
}
In controllers, I am setting prescription as req.file
import historyCard from "../models/historyCard.js";
export const medicalHistory = async(req,res,next) =>{
console.log(req.file);
try{
// console.log(req.body);
const newRecord = new historyCard({
"diseasename":req.body.diseasename,
"checkdate":req.body.checkdate,
"weight":req.body.weight,
"height":req.body.height,
"desc":req.body.desc,
"prescription":req.prescription
})
console.log(newRecord);
await newRecord.save();
res.status(200).send(newRecord);
}catch(err){
next(err);
}
}
This is how i have imported multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./public/images");
},
filename: (req, file, cb) => {
cb(
null,
Date.now()+file.originalname
);
},
});
const upload=multer({
storage:storage,
limits:{
fieldSize:1024*1024*3
}
})
const router = express.Router();
router.post('/addrecord',upload.single('image'),medicalHistory);
In database I am storing prescription as
prescription:{
type:String,
default: ""
}
But the thing that is happening is , it is not being in my public folder.
well, you're not sending the file.
You need to use FormData to send the file, and by using its .append method, not an object, with URL.createObject
You can append the file separately, as well as recordData object by stringifying it, and then parse it on the server.
Since the file is stored in a public folder, you can store path to the file as a value of prescription property.
You can then add that property to the parsed recordData object on the server once the file is uploaded (and because only there can you know the file path, because of the custom filename), and then save the whole object:
try this:
on the client upload:
const handleClick = async (e) =>{
e.preventDefault();
console.log(selectedImage.name);
// add data, without prescription
const recordData =
{
diseasename,weight,height,medicines,desc,checkdate,patientId
}
// use FormData
const formData = new FormData();
// append JSON data
formData.append('recordData', JSON.stringify(recordData));
// append file. the name should match the one on multer: upload.single('image'),
formData.append('image', selectedImage);
try{
const res = await axios({
method: "POST",
url: "http://localhost:3000/api/record/createrecord",
data: formData, // send formdata
withCredentials: false
});
console.log(res.data);
}
catch(err){
console.log(err);
}
}
and on the controller parse recordData, and add file path as prescription's property value:
const recordData = JSON.parse(req.body.recordData);
// save only path to the file, since it's in the public folder
recordData.prescription = req.file.path;
console.log('recordData', recordData);
// save..
const newRecord = new historyCard(recordData);

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');
});
}
...

Node / React : I can't upload an image with my post with multer

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()
};

Why does my DataForm not return as an object that my URL.createObjectURL is supposed to read?

All I am trying to accomplish is to give my users the option to upload images. I decided to use mongoDB as my database which means I must store photos locally and then send them to the DB. As far as I know, I am new. The object created by ImageName and ImageData isn't being passed properly to my axios post request
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
Here is my route
const Image = require('../models/imageModel');
const ImageRouter = express.Router();
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb){
cb(null, './uploads/')
},
filename: function(req,file,cb){
cb(null, Date.now() + file.originalname);
}
});
const fileFilter = (req, file, cb) =>{
if(file.mimetype === 'image/jpeg' || file.mimetype ==='image/png'){
cb(null, true);
} else{
//rejects storing file
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits:{
fileSize:1024 *1024 * 5
},
fileFilter: fileFilter
});
// stores image in uploads folder using multers and creates a reference to the file
ImageRouter.route("/upload")
.post(upload.single('imageData'), (req, res, next) => {
console.log(req.body);
const newImage = new Image({
imageName: req.body.imageName,
imageData: req.file.path
});
newImage.save()
.then((result)=>{
console.log(result)
res.status(200).json({
success: true,
document: result
});
})
.catch((err)=> next(err))
});
module.exports = ImageRouter;
here is my model
const Schema = mongoose.Schema;
const ImageSchema = new Schema({
imageName:{
type: String,
default: "none",
required: true
},
imageData: {
type :String,
required: false
}
});
const Image = mongoose.model('Image' , ImageSchema)
module.exports = Image;
Here is my ImageUploader page that calls the function
import axios from 'axios';
import DefaultImg from '../assets/default-img.jpg';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class ImageUploader extends Component {
constructor(props) {
super(props)
this.state = {
multerImage: DefaultImg
}
};
setDefaultImage = (uploadType) => {
if (uploadType === "multer") {
this.setState({multerImage: DefaultImg});
};
};
// function to upload image once it has been captured include multer and
// firebase methods
uploadImage(e, method) {
let imageObj = {};
if (method === "multer") {
let imageFormObj = new FormData();
imageFormObj.append("imageName", "multer-image-" + Date.now());
imageFormObj.append("imageData", e.target.files[0]);
console.log(imageFormObj)
// stores a readable instance of the image being uploaded using multer
this.setState({
multerImage: URL.createObjectURL(e.target.files[0])
});
axios
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
.catch((err) => {
alert("Error while uploading image using multer");
this.setDefaultImage("multer");
});
}
};
render() {
return (
<div className="main-container">
<h3 className="main-heading">Image Upload App</h3>
<div className="image-container">
<div className="process">
<h4 className="process__heading">Process: Using Multer</h4>
<p className="process__details">Upload image to a node server, connected to a
MongoDB database, with the help of multer</p>
<input
type="file"
display="block"
className="process__upload-btn"
placeholder="Username"
onChange={(e) => this.uploadImage(e, "multer")
}/>
<img
src={this.state.multerImage}
alt="upload-image"
className="process__image"/>
</div>
</div>
</div>
);
};
};
and finally this is where it's being rendered
// import {EditProfile} from './EditProfile'
import DisplayCats from '../cats/DisplayCats'
// import Button from '#material-ui/core/Button';
import Axios from 'axios';
import ImageUploader from '../ImageUploader';
function ProfilePage(props) {
console.log(props.userInfo)
const user = props.userInfo
console.log(user)
console.log(user._id)
console.log(props.userInfo._id)
let [responseData,
setResponseData] = useState('');
// getLocation = () => {
// navigator
// .geolocation
// .getCurrentPosition(function (position) {
// console.log(position)
// });
// }
const clickHandler = (e) => {
this
.props
.history
.push('/DisplayCats')
}
// const setProfileImage = (event) => {
// Axios
// .post('http://localhost:5000/api/users/updateImage/' + user._id, {
// "_id": user._id,
// "profileImage": event.target.value
// })
// .then(res => {
// setResponseData(res.data)
// console.log(responseData)
// }, function (err) {
// console.log(err)
// })
// }
return (
<div style={{
color: "black"
}}>
<h5>This is {props.userInfo.firstName}'s Profile Page</h5>
<h5>Last name: {props.userInfo.lastName}</h5>
<h5>Age: {props.userInfo.age}</h5>
<h5>Location:{props.userInfo.location}</h5>
<h5>Image:{props.userInfo.profileImage}</h5>
<h5>Biography:{props.userInfo.biography}</h5>
<ImageUploader user={user}/>
{/* <div className="col-md-6 img">
<img
src={responseData.profileImage}
alt="profile image"
className="img-rounded"/>
</div> */}
<div className="row">
<DisplayCats user={user}/>
</div>
{/*
<Button
variant="outlined"
color="primary"
onClick={this.clickHandler}
component={this.EditProfile}
user={this.props.user}>
Edit Info
</Button> */}
</div>
)
}
export default ProfilePage;
I want my image data and and image name to be created into a URL for my other functions to read it. My error comes back as POST /api/image/uploadmulter/ 404 97.290 ms - 163
Perhaps im missing something but from the code you shared, you set your route as:
ImageRouter.route("/upload")
so, your client side code should be posting to: http://localhost:5000/upload
yet, your code does this:
.post(`http://localhost:5000/api/image/uploadmulter/`
You're getting a 404 error, which makes sense since this route hasn't been defined.
On a related note, I'd recommend using something like react-uploady, to manage the uploads on your client-side. It will save you a lot of code and bugs. Especially if you want to show preview or other related functionality (like: progress, cancel, retry, etc.).

req.files is undefined using express-fileupload

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)

Resources