File upload with react, nodeJS multer doesn't work - node.js

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

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

React NodeJS Multer file upload failure

Im trying to single file upload to folder public/files, using reactJS as frontend, multer nodeJS as serverside :
react :
here i have file input that accepts only pdf and msword, and on change it sets the file state, on submit it triggers function handleCareer which makes formData object and appends data from state, the file.name will be something like 1663010450031report.pdf, and then make request to /upload.
const [file, setFile] = useState(null);
const handleCareer = async () => {
const data = new FormData();
const pdf = `${Date.now()}${file.name}`;
data.append("file", pdf);
try {
await fetch("http://localhost:8000/upload", {
method: "POST",
body: data,
});
} catch (err) {
console.log(err);
}
};
<div className="uploadCV">
<label htmlFor="resumeFile" className="downloadImg">
<AddAPhotoIcon className="postIcon" />
<span>Upload resume</span>
</label>
<input
type="file"
accept="application/pdf,application/msword"
id="resumeFile"
name="file"
onChange={(e) => setFile(e.target.files[0])}
></input>
</div>
<div className="submitUpload">
<button type="button" onClick={handleCareer}>
Request
</button>
</div>
server :
here im using path public/files as the target folder for upload and specifying in destination as well as specifying req.body.file for filename
app.use("/files", express.static(path.join(__dirname, "public/files")));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/files");
},
filename: (req, file, cb) => {
cb(null, req.body.file);
},
});
const upload = multer({ storage: storage }).single("file");
app.post("/upload", function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
console.log(err);
} else if (err) {
console.log(err);
}
return res.status(200).send(req.file);
});
});
but the file is not uploading, and it doesn't console log any errors, on postman it gives empty result with status code 200 .. what's is the problem here ?
You haven't send the file field to backend. You have to send a file (like pdf). Now you are sending only a pdf name. There aren't any file.
const upload = multer({ storage: storage }).single("file");
Here you have defined the name of incoming data is file. So you should add the field which named file

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

File Upload from API Routes Next.js not working

I'm trying to upload a file to my server which I'm successfully able to do with my code, but I get a this console output from it:
API resolved without sending a response for /api/upload, this may result in a stalled requests.
The file is successfully put into the folder though.
I don't quite understand how I am not sending a response as to me I am.
What am I doing wrong?
Here is my form code:
const axios = require("axios").default;
class VideoUploadForm extends React.Component {
constructor(props) {
super(props);
this.state = {
file: null,
uploaded: false
};
}
onChangeHandler = event => {
this.setState({
selectedFile: event.target.files[0],
loaded: 0
});
};
onClickHandler = () => {
const data = new FormData();
data.append("video", this.state.selectedFile);
axios
.post("/api/upload", data, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
};
render() {
return (
<div>
<form
action="#"
method="post"
encType="multipart/form-data"
target="transFrame"
>
<input type="file" name="video" onChange={this.onChangeHandler} />
<button onClick={this.onClickHandler}>upload</button>
</form>
<iframe
width="200"
height="100"
name="transFrame"
id="transFrame"
></iframe>
</div>
);
}
}
export default VideoUploadForm;
and the API
import multer from "multer";
export const config = {
api: {
bodyParser: false
}
};
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, "public");
},
filename: function(req, file, cb) {
cb(null, "video.mp4");
}
});
var upload = multer({ storage: storage });
export default async (req, res) => {
upload.single("video")(req, {}, err => {
res.send(req.file.path);
res.end();
console.log(req.file); // do something with the file
});
};
This is where the problem is
res.send(req.file.path);
res.end();
res.send implicitly calls res.write followed by res.end. You should remove the second res.end.
Try
var upload = multer(storage);

Resources