How I upload an image file from a form in React to an Express api? - node.js

I try to modify an user profile Image, but my file seems to not arrive in my backend.
I was able to do so with Postman, but not with my frontend.
I use Express with Mongoose and MongoDB for backend. In frontend I use React, js, Redux and Axios.
In my schema, the profile Image is like this:
profileImg: {
data: Buffer,
contentType: String
}
Later, I use the buffer to make the link for image in frontend.
For working with files, I use Multer. This is what I use for my middleware:
const multer = require('multer');
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 storage = (location) => multer.diskStorage({
destination: function(req, file, cb) {
cb(null, `./uploads/${location}`);
},
filename: function(req, file, cb) {
cb(null, new Date().toISOString().replace(/:/g, '-') + file.originalname)
}
})
const uploadImg = (location) => multer({
storage: storage(location),
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
})
module.exports = uploadImg
This is where I make the update of the user in backend. You can change the name if you comment the section for profileImg section inside updatedUser.
In profileImg I have 2 data lines:
The commented one is what I tried for my file object
The active one is working for image files that are sent with Postman
const updatedUser = await User.findByIdAndUpdate(
req.user.id,
{
name: req.body.name,
profileImg: {
data: fs.readFileSync("uploads/profile/" + req.file.filename),
//data: fs.readFileSync("uploads/profile" + req.body.profileImg.name)
contentType: "image/jpeg",
},
},
{ new: true }
);
res
.status(200)
.json({
_id: updatedUser.id,
name: updatedUser.name,
email: updatedUser.email,
profileImg: updatedUser.profileImg,
token: generateToken(updatedUser._id),
// body: req.body,
// file: req.file
});
});
In frontend I save the changes of the inputs in a variable called changedUser and it looks like this:
const handleChange = (e) => {
if (e.target.name === "profileImg") {
// changeuser({ ...userChanged, [e.target.name]: fileUploadHandler(e.target.id) });
changeuser({ ...userChanged [e.target.name]:document.getElementById('profileImg').files[0]});
} else {
changeuser({ ...userChanged, [e.target.name]: e.target.value });
}
};
This is my submit function (I use dispatch because my project is using redux):
const handleSubmit = (e) => {
e.preventDefault();
initialData.name = userChanged.name;
initialData.address = userChanged.address;
initialData.profileImg = userChanged.profileImg
dispatch(update(userChanged))
changeVisibility("hidden-buttons");
};
I'll also put here the form with the section in which is found the file input
<Container fluid className="body-loginPage">
<form onSubmit={handleSubmit} id="formElem" encType="multipart/form-data">
<Row className="custom-row mb-5">
<Col xs={6} className="custom-column">
{/* <img src={`${profile_pic}`}></img> */}
<Image
thumbnail
rounded
src={`data:image/png;base64,${base64_String()}`}
/>
</Col>
</Row>
<Row className="m-1 custom-row mb-3">
<Col xs={3}>
<div>Nume: </div>
</Col>
<Col xs={7}>
<input
name="name"
value={userChanged.name}
onChange={handleChange}
/>
</Col>
</Row>
<Row className="m-1 custom-row mb-3">
<Col xs={3}>
<div>Email: </div>
</Col>
<Col xs={7}>
<input
disabled
name="address"
value={email}
onChange={handleChange}
/>
</Col>
</Row>
<Row className="m-1 custom-row mb-3">
<Col xs={3}>
<div>Change picture: </div>
</Col>
<Col xs={7}>
<input
id="profileImg"
type="file"
name="profileImg"
//value={userChanged.picture !== [] ? userChanged.picture : ""}
ref={inputRef}
onChange={handleChange}
/>
{/* <input type="file" onChange={fileSelectedHandler} /> */}
</Col>
</Row>
<Row className="m-1 custom-row">
<Col xs={3}>
<div>Telefon: </div>
</Col>
<Col xs={7}>
<input
name="phone"
value={userChanged.phone}
onChange={handleChange}
/>
</Col>
</Row>
<Row className="m-1 custom-row">
<Col xs={6} />
<Col xs={2}>
<Button
variant="danger"
onClick={cancelEdit}
className={`${visible}`}
>
Cancel
</Button>
</Col>
<Col xs={3}>
<Button
variant="success"
className={visible}
type="submit"
>
Actualizeaza
</Button>
</Col>
</Row>
</form>
<button onClick={getInfo}>click</button>
</Container>
I've tried many variants. I had success using Postman, but I can't reproduce the same result from my project. I am not very experienced with forms and maybe this is where my problem is. I also tried formData for my first time and it didn't work very well.
I am opened to make changes in backend if necessary. I am struggling with this problem for 2 days, any help is appreciated.
If my question is unclear, please ask me for more details. I am quite new here, on stackoverflow.

Related

how to post form data to the server backend

Form.js
import "./form.css";
import React, {useEffect,useState} from "react";
import {addBeauty,deleteB} from "./beauty";
import Modal from "./Modal";
import axios from 'axios';
export default function CreateForm() {
const [Name, setName] = useState("");
const [Intro, setIntro] = useState("");
const [isOpen, setIsOpen] = useState();
const [backendData, setBackendData] = useState([{}]);
useEffect(()=>{
fetch("http://localhost:5000/api").then(
response=>response.json()
).then(
data=>{ setBackendData(data)}
)
},[]);
const handleSubmit = (event)=>{
event.preventDefault();
axios.post("http://localhost:5000/api",{
id: userList[userList.length - 1].id + 1, Name:Name, Introduction:Intro
}).then(res=>{
console.log(res.data);
})
}
return (
<div className="container">
<form className="add" onSubmit={handleSubmit} >
<h2>User</h2>
<label htmlFor= "name">Name</label>
<input type="text" value={Name}
onChange={(event) => {setName(event.target.value);}}/>
<label htmlFor= "Intro">Introduction</label>
<input type="text" value={Intro}
onChange={(event) => {setIntro(event.target.value);}}/>
<p></p>
<p></p>
<div className = "press">
<button id = "1" type="submit">
Add Beauty
</button>
<button id = "2"
onClick={clearForm}
>
Clear Form
</button>
</div>
</form>
<br></br>
<br></br>
<br></br>
<div className="display">
{(typeof userData.user1 === 'undefined')?(
<h1>Loading</h1>):(
backendData.user1.map((user,i)=>{
return (
<div>
<h1> {user.Name}</h1>
<button onClick={()=>{
setIsOpen(user.id);
}}>View in popup</button>
<Modal open={isOpen === user.id} onClose={()=>setIsOpen(undefined)}>
<h3> {User.Introduction}</h3>
</Modal>
</div>
);})
)}
</div>
</div>
);
}
Server.js
const express = require('express');
const app = express();
const cors=require("cors");
const corsOptions ={
origin:'*',
credentials:true, //access-control-allow-credentials:true
optionSuccessStatus:200,
}
app.use(cors(corsOptions)) // Use this after the variable declaration
app.get("/api",(req,res)=> {
res.json({"user1":[
{
id: 1,
Name: "Isabella",
},
{
id:2,
Name: "Catalina
}
]})
});
app.listen(5000,()=>{
console.log("Server started on port 5000");
})
I create a from using react. And I try to send the formdata to backend and insert the formdata into the data stored at backend using axios.post. But it doesn't work. I know it's because I didn't add the prefix of backend data "user1" in axios.post. But I am not sure how to do that. Could anyone help me with this?
You have not created the route on the server correctly. You have opened a route for GETting "/api" but you need to open a route for POSTing
Replace this line:
app.get("/api",(req,res)=> {
with
app.post("/api",(req,res)=> {
Hi Here you need to create one route for post API as below
app.post("/api",(req,res)=> {
console.log(req.body) //here you got the requested data.
res.send("Success !");
});

How i can update the image url if user select new file other wise image url not update in nodejs

I'm creating a book project where i'm saving the books images into the cloudinary and there url's saving into the mongodb database which are working well.But i'm facing issue during the updation of a book when i update my book then the url of book is not updated and console giving me error Cannot read properties of undefined (reading 'map') where i want to update the url with new one url of image but its not working Please any one can solve this
this is my update.js code
module.exports.updateBook = async (req, res) => {
try {
const { id } = req.params;
const book = req.body;
const singleBook = await Book.findById(id);
// Delete Prvious Url From the Cloudinary and Reset It to the new ..
cloudinary.v2.uploader.destroy(singleBook.image[0].filename);
book.image = req.files.map((f) => ({
url: f.path,
filename: f.filename,
}));
console.log("Single Book ===", singleBook);
const updateBook = await Book.findByIdAndUpdate(
id,
{ $set: book },
{ new: true }
);
if (updateBook) {
res
.status(200)
.json({ success: true, message: "Book Updated Successfully!" });
} else {
res.status(400).json({
success: false,
message: "Book Not Updated There Is an error!",
});
}
} catch (err) {
console.log("** Error In Update Book **", err.message);
}
};
this is my route handler
const express = require("express");
const router = express.Router();
const book = require("../controller/book");
const authenticated = require("../middleware/verifyToken");
const multer = require("multer");
const { storage } = require("../cloudinary");
const upload = multer({ storage });
// Update Book By ID
router.route("/:id").put(authenticated, upload.array("image"), book.updateBook);
module.exports = router;
this is my reactjs update method
const formik = useFormik({
initialValues: {
title: book?.title,
author: book?.author,
price: book?.price,
description: book?.description,
image: book?.image[0].url,
},
validationSchema: validationSchema,
enableReinitialize: true,
onSubmit: (values) => {
const formData = new FormData();
formData.append("title", values.title);
formData.append("price", values.price);
formData.append("description", values.description);
formData.append("author", values.author);
formData.append("image", values.image);
Axios.put(`${Base_URL}/book/${id}`, values, {
headers: {
Authorization: authHeader(),
},
})
.then((res) => {
if (res.data.success) {
message = res.data.message;
setAlertContentupdate(message);
setAlertupdate(true);
setTimeout(() => {
handleClose();
navigate(`/book/${id}`);
getBook();
console.log("Response == ", res.data.message);
}, 3000);
}
})
.catch((err) => {
console.log("Error ====", err.message);
});
},
this is my jsx code for updating book
<form onSubmit={formik.handleSubmit}>
<TextField
name="title"
autoFocus
margin="dense"
label="Book Title"
type="text"
fullWidth
variant="standard"
value={formik.values.title}
onChange={formik.handleChange}
error={formik.touched.title && Boolean(formik.errors.title)}
helperText={formik.touched.title && formik.errors.title}
/>
<TextField
name="author"
margin="dense"
label="Book Author"
type="text"
fullWidth
variant="standard"
value={formik.values.author}
onChange={formik.handleChange}
error={formik.touched.author && Boolean(formik.errors.title)}
helperText={formik.touched.author && formik.errors.author}
/>
{/* File Input Field */}
{/* Picture Input */}
<input
type="file"
name="image"
accept=".png, .jpeg, .jpg"
onChange={(e) => {
formik.setFieldValue("image", e.target.files[0]);
}}
/>
{formik.touched.image && formik.errors.image ? (
<div style={{ color: "#e53935", fontSize: "12px" }}>
{formik.errors.image}
</div>
) : null}
{/* Price Input Field */}
<TextField
name="price"
margin="dense"
label="Book Price"
type="text"
fullWidth
variant="standard"
value={formik.values.price}
onChange={formik.handleChange}
error={formik.touched.price && Boolean(formik.errors.price)}
helperText={formik.touched.price && formik.errors.price}
/>
<TextField
name="description"
margin="dense"
label="Book Description"
type="text"
fullWidth
variant="standard"
value={formik.values.description}
onChange={formik.handleChange}
error={
formik.touched.description &&
Boolean(formik.errors.description)
}
helperText={
formik.touched.description && formik.errors.description
}
/>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button type="submit">Update</Button>
</DialogActions>
</form>
In formik i'm getting the book data from back end api's and putting into the formik initial values But Problem is that when i clicked on the update button then the backend compiler giving me this error Cannot read properties of undefined (reading 'map') Please any one can solve this thanks in advance
So this line looks like the issue for me:
cloudinary.v2.uploader.destroy(singleBook.image[0].filename);
Using this is actually deleting your asset so you are probably want to just update it using the explicit API. See https://cloudinary.com/documentation/image_upload_api_reference#explicit
So maybe something like:
cloudinary.v2.uploader.explicit(singleBook.image[0].filename);
Let me know if this helps?

States in react not getting updated

Inside the verifyPassword function when I am trying to update the state of user using setUser, it is not updating. Therefore the values of input remain the same and also when I console log user it is empty.
I have also tried by first storing the res object from .done method in another variable and then update the state, but that didn't work too.
below is the code.
import React , {useState} from "react";
import $ from "jquery";
function ChangeDetails(props){
var [pass , setPass] = useState("");
var [user , setUser] = useState({
name:"",
phone:"",
email:""
});
var [auth ,setAuth] = useState(false);
function passChange(e){
let password = e.target.value;
setPass(password);
}
function verifyPassword(event){
event.preventDefault();
$.post("http://localhost:4000/details" , {username: sessionStorage.getItem("User") , password: pass})
.done((res)=>{
let {name , phone, email} = res;
console.log(name);
setUser=({
name:name,
phone:phone,
email:email
})
console.log(user);
console.log(res);
setAuth(true);
})
.fail(e=>{console.log(e);})
}
function handleChange(e){
let {name , value} = e.target;
setUser(prevValue=>{
return {
...prevValue,
[name] : value
}
})
}
return (
<div>
<h1>Change Your Details here #{sessionStorage.getItem("User")}</h1>
{!auth && <form onSubmit={verifyPassword}>
<h2>Verify by entering Password</h2>
<input onChange={passChange} value={pass} type="password" name="password" />
<button>Submit</button>
</form>}
{auth && <form>
<label>Name:
<input type="text" onChange={handleChange} value={user.name} name="name" id="name" /></label><br />
<label>Phone Number:
<input type="text" onChange={handleChange} value={user.phone} name="phone" id="phone" /></label><br />
<label>Email:
<input type="email" onChange={handleChange} value={user.email} name="email" id="email" /></label><br />
<button>Change</button>
</form>}
</div>
);
}
export default ChangeDetails;
you got typo on setUser within verifyPassword function
you typed setUser=
it should be setUser()
Because setUser is a funtion to update state. Use
setUser({
name:name,
phone:phone,
email:email
})
instead of
setUser = ({
name:name,
phone:phone,
email:email
})

Pass a valid time type usnig moment from React setState to postgres

I am new in coding and essentially in React. I am trying to create a human resource management system that will have an employee and an admin. I am now working on using an axios to post to knex postgres as db and nodejs.
I need help to pass in a correct value with format of "HH:mm:ss" to my backend taking time type.
This is my knex migration:
exports.up = function(knex) {
return knex.schema.createTable('undertime_overtime', (table) => {
table.increments('id').primary();
table.date('date_filed').notNullable(); //has to be default date now?
table.time('from_time').notNullable();
table.time('to_time').notNullable();
table.string('reason').notNullable();
table.integer('time_type').defaultTo(1);
table.boolean('isDeleted').defaultTo(0);
table.boolean('isAccepted').defaultTo(0);
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('modified_at').defaultTo(null);
table.integer('created_by').unsigned().notNullable();
table.foreign('created_by').references('employees.id');
});
Here are the things I tried that did not work:
state = {
date_filed: new Date(),
from_time: moment().format("HH:mm:ss").toString(),
to_time: moment().format("HH:mm:ss"),
reason: '',
time_type: 1,
created_by: 1 //todo
};
handleFromTime = time => {
this.setState({
from_time: time.format("HH:mm:ss")
});
console.log(time("HH:mm:ss"));
};
Here is my component:
import React, { Component } from 'react';
import moment from 'moment';
import { Content, Row, Col, Box, Button } from 'adminlte-2-react';
import TimePicker from 'rc-time-picker';
import DatePicker from "react-datepicker";
import axios from 'axios'
import 'rc-time-picker/assets/index.css';
class OvertimeComponent extends Component {
state = {
date_filed: new Date(),
from_time: moment(),
to_time: moment(),
reason: '',
time_type: 1,
created_by: 1 //todo
};
handleChangeDateFiled = date => {
this.setState({
date_filed: date
});
console.log(date)
};
handleFromTime = time => {
this.setState({
from_time: time
});
console.log(time);
};
handleToTime = time => {
this.setState({
to_time: time
});
console.log(time.format('HH:mm:ss'));
};
handleReason = event => {
this.setState({
reason: event.target.value
})
console.log(event.target.value);
}
handleSubmit = event => {
console.log(`date-> ${this.state.date_filed} from ${this.state.from_time} to ${this.state.to_time} reason ${this.state.reason}`)
event.preventDefault()
axios.post('http://localhost:8080/api/time',this.state)
.then(response=> {
console.log(response);
}).catch(error => {
console.error(error);
})
}
footer = [
<Button key="btnSubmit" type="success" pullRight text="Submit" onClick={this.handleSubmit} />,
];
render() {
return (
<Content title="Overtime" subTitle="Requests" browserTitle="Overtime">
<Row>
<Col md={6}>
<Row>
<Col xs={12}>
<Box title="Overtime Application" type="primary" collapsable footer={this.footer}>
<div className="form-group">
<label>Date</label>
<div>
<DatePicker name="date_filed" selected={this.state.date_filed} onChange={this.handleChangeDateFiled}/>
</div>
</div>
<div className="form-group">
<label>From</label>
<div>
<TimePicker name="from_time" value={this.state.from_time} onChange={this.handleFromTime} />
</div>
</div>
<div className="form-group">
<label>To</label>
<div>
<TimePicker name="to_time" value={this.state.to_time} onChange={this.handleToTime} />
</div>
</div>
<div className="form-group">
<label>Reason</label>
<textarea type="text" name="reason" value={this.state.reason} onChange={this.handleReason} className="form-control" placeholder="Enter ..." />
</div>
</Box>
</Col>
</Row>
</Col>
<Col md={6}>
<Box title="Request Status" type="primary" collapsable>
<div className="form-group">
<label>todo</label>
</div>
</Box>
</Col>
</Row>
</Content>);
}
}
export default OvertimeComponent;
I found the issue. I should've touched the axios post to get the format I wanted from the moment object.
axios.post('http://localhost:8080/api/time',{
'date_filed':this.state.date_filed,
'from_time':this.state.from_time.format('HH:mm:ss'),
'to_time':this.state.to_time.format('HH:mm:ss'),
'reason':this.state.reason,
'created_by': 1 //todo
})

Node Express upload file with additional data

I am new to Node JS want to create Rest API for Upload Image and facing this issue.
I want to create a post method with multiple files and additional data from inputs. This is my code:
index.js :
app.post('/upload-photos', upload.array('photos'), function (req, res) {
const uploadInfo = req.files.map(file => {
return {
sourceName: file.originalname,
newName: file.filename
};
});
res.send(uploadInfo);
});
My issue is I want to add some form data like (name, address, phone), has anyone else experienced this, please help me.
Thanks.
When using multer additional fields can be accessed through req.body.
app.post('/upload-photos', upload.array('photos'), function (req, res) {
const { name, address, phone } = req.body;
const uploadInfo = req.files.map(file => {
return {
sourceName: file.originalname,
newName: file.filename
};
});
res.send(uploadInfo);
});
In your form:
<form action="/upload-photos" method="post" enctype="multipart/form-data">
<input type="file" name="photos" multiple />
<input type="text" name="name" />
<input type="text" name="address" />
<input type="text" name="phone" />
</form>

Resources