Upload file with React - reactjs

I want to make a simple file upload form on the front end. Then, on the backend, I would pass the information about that file to an API.
Here is my front-end code where I call a specific function on the back end and pass the data:
import React from 'react';
import Axios from 'axios';
const Upload = () => {
// a local state to store the currently selected file.
const [selectedFile, setSelectedFile] = React.useState(null);
const handleSubmit = async (event) => {
event.preventDefault()
//Got all the Infos about my file
console.log(selectedFile)
const formData = new FormData();
formData.append("selectedFile", selectedFile);
//Empty result
console.log(formData)
Axios.get("http://localhost:3001/upload", {
//I will pass the data to a function in the backend
params: {
data: formData,
},
})
.then((Response) => {
console.log(Response)
})
.catch(function (error) {
console.log(error);
});
}
const handleFileSelect = (event) => {
setSelectedFile(event.target.files[0])
}
return (
<form onSubmit={handleSubmit}>
<input type="file" onChange={handleFileSelect}/>
<input type="submit" value="Upload File" />
</form>
)
};
export default Test
On the back-end side, a route call the method
router.get('/upload?', Upload);
Then finally the function in the backend to process
const ApiProcess = (req, res) => {
var axios = require('axios');
var data = req.query
console.log(req.query)
//All the API Stuff
}
But the problem is that I receive empty data in the Backend. What's wrong with my code?
Thanks
EDIT
On backend side I use multer and add 'app.use(multer().any())' on top of index file. That help cause now I cant access in backend to a simple formData. Now my function that receive the data log this '[Object: null prototype] {}'
Any idea ?

This is because your file is not getting forwarded from frontend
use FileReader instead
<input type="submit" value="Upload File" onChange={(e) =>
setFile(e.target.files)} />
const data = new FormData();
data.append(file[0])
and then you can access the file data on file[0] index and after storing the data you can forward it to the backend

there are some problems in your code.
first of all an upload request usually is a post type. and also you should send Content-Type header with your request. so:
Axios.post("http://localhost:3001/upload", formData {
headers: {
'Content-Type': 'Multipart/formData',
},
})
when you log formData it's always empty. you can use some methods like formData.keys() or formData.values() to see inside it.

Ok I got the solution. I missed a piece of middleware to process Multipart/formdata on Express Side :
const router = express.Router();
const multer = require("multer");
//Set the destination folder and Naming of the file that is upload
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer({ storage: storage })
Then I process the formData with the files
router.post('/upload', upload.array("file"),Upload);
Thanks a lot for your help

Related

Sending formdata from React to express using multer

This is a MERN app that has a form consisting of text inputs and a file input. The problem: I am unable to access the file input in my backend.
Start of controller function:
async function create(req, res) {
function base64_encode(file) {
let bitmap = fs.readFileSync(file)
return bitmap.toString('base64')
}
console.log(req.file)
let image = base64_encode(req.file.path)
The console.log of req.file returns "undefined" and I get error message "Cannot read properties of undefined (reading 'path')" referring to req.file.path.
The route:
const multer = require("multer")
const upload = multer({ dest: '../../uploads/'})
router.post("/", upload.single('image'), spotCtrl.create)
It should be noted that the image file is not showing up in the image folder I created.
The handleSubmit function:
handleSubmit = async () => {
let chosenImage = this.state.image
const formdata = new FormData()
formdata.append('image', chosenImage)
let body = {
name: this.state.name,
description: this.state.description,
address: this.state.address
}
let options = {
method: 'POST',
body: JSON.stringify(body),
formdata: formdata
}
await fetch("/api", options)
.then(res => res.json())
}
The <form tag has encType="multipart/form" set as well.
I've installed multer, fs, express-fileupload, among others
A couple maybe relevant lines from my server.js:
app.use(express.json(), expressfileupload());
app.use(express.static(path.join(__dirname, 'build')));
I have tried in my controller function to use req.files instead of req.file however I get the same result.

Uploading React - Node image(s) to Cloudinary

Has anyone uploaded an image using Node, React, Antd library to cloudinary? I'm getting an error saying that the file is missing. I'm not sure if I should be sending the entire file object or just the name. I have sent the thumburl (sometimes it works others it doesn't there has to be something I'm doing wrong).
This is my backend
const uploadImage = async (req, res) => {
try {
const result = await cloudinary.uploader.upload(req.body.image, {
public_id: `${Date.now()}`,
resource_type: 'auto' //jpeg or png
});
res.json({
public_id: result.public_id,
url: result.secure_url
});
} catch (error) {
console.log(error);
res.status(400).send(error)
}}
This is my frontend:
const Uploader = () => {
const { token } = useSelector(state => state.user);
const handleChange = ({file}) => {
console.log(file)
axios.post(`${process.env.REACT_APP_API}/uploadImages`, {file}, {
headers:{
authtoken: token
}
})
.then(res => console.log(res))
.catch(err => console.log(err))
};
return (
<Upload
listType="picture"
showUploadList
onChange={handleChange}
>
<Button>Upload</Button>
</Upload>
)
}
export default Uploader
IDK why the onChange triggers 3 times when it has worked I've sent the thumbUrl and it uploads 3 times, I have seen that I can use beforeUpload but I believe this works before uploading. I want to upload, preview the image and sending it to the server and then send it to my Form Component to add it to the values I have there
Anyone who has already uploaded could help or any ideas would be appreciated?
When it comes to the file not uploading properly, I am guessing it is because the res.body.image isn't the actual file. I would look at what the value is there. I would guess you are missing a middleware.
As far as your frontend issue, I'm still a little unclear about what the issue you are having exactly is. For an example of a clean frontend upload I would check out https://codesandbox.io/embed/jq4wl1xjv. You could also consider using the upload widget which will handle the preview as well as give you some easy editing options.
https://cloudinary.com/documentation/upload_widget
I was able to figure it out, indeed there is a need of a middleware, I used formidable on the routes of my backend
router.post('/uploadImages', authCheck, adminCheck, formidable({maxFileSize: 5 * 1024 * 1024}), imageUpload);
And fixed the controller
const imageUpload = async (req, res) =>{
try {
//console.log('req files:', req.files.file.path)
const result = await cloudinary.uploader.upload(req.files.file.path, {
public_id: `${Date.now()}`,
resource_type: 'auto'
});
res.json({
public_id: result.public_id,
url: result.secure_url
})
} catch (error) {
console.log(error)
}
}
As far as the Frontend goes. Ant designs component takes an action that makes the POST to the backend, and it also takes headers if needed (in my case it takes an authtoken for firebase).
When the image is uploaded it makes the POST to the backend based on the "action". The response will bring back the "FileList" with the upload URL to access it.
<Upload
listType="picture"
showUploadList
multiple
action={`${process.env.REACT_APP_API}/uploadImages`}
headers={{authtoken: token}}
>
<Button>Upload Images</Button>
</Upload>
I hope this helps somebody else too
I tried this and it's worked for me.
Here I used Multer middleware for handling form data(for uploading files).
I used this route,
router.post('/uploadImages', authMiddleware, multer.single("image"), imageUpload);
Corrected controller,
try {
let result;
if (req.file) {
result = await cloudinary.uploader.upload(req.files.file.path, {
public_id: `${Date.now()}`,
resource_type: 'auto'
});
}
res.json({
public_id: result.public_id,
url: result.secure_url
})
} catch (error) {
console.log(error)
}
}
//Multer config file,
const multer = require("multer");
const path = require("path");
module.exports = multer({
storage: multer.diskStorage({}),
fileFilter: (req, file, cb) => {
let ext = path.extname(file.originalname);
if (
ext !== ".jpg" &&
ext !== ".jpeg" &&
ext !== ".png" &&
ext !== ".PNG" &&
ext !== ".JPG" &&
ext !== ".JPEG"
) {
cb(new Error("File type is not supported"), false);
return;
}
cb(null, true);
},
});

How do I specify a specific get method using axios?

I am currently working with a yarn React application, and one of my front-end components has a form that sends a get request to the backend.
import { useState } from "react";
import axios from "axios";
export default function fld(props){
//set the inital state of the name as null
const [songName, setSongName] = useState(null);
//this function is linked to the GET request,
const handleRetrieve = async (event) =>{
//prevent the page from reloading
event.preventDefault();
//set the formData
const formData = new FormData();
formData.append("songName", songName)
try{
const response = await axios({
method: "get",
url: "http://localhost:8080/",
data: formData
});
console.log(response)
}catch(error){
console.log(error)
}
};
const handleNameSelect = (event ) =>{
setSongName(event.target.name[0]);
};
return(
<form onSubmit={handleRetrieve}>
<label>List out the songs</label>
<input type="text" onChange={handleNameSelect}/>
<input type="submit"/>
</form>
)
}
The thing is, I have multiple get requests in the backend, but only want to fire off the second one.
FIRST GET REQUEST:
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
SECOND GET REQUEST (the one I want to fire)
app.get("/Uploadedfiles/:name", (req, res) => {
console.log("GET method: Uploadedfiles/:name")
const params = req.params.name;
let red = read(params);
console.log("reading from folder");
res.send(red);
});
help pls :(
The only thing you need is to add the endpoint path to the config object in axios like this:
const response = await axios({
method: "get",
url: `http://localhost:8080/Uploadedfiles/${songName}`,
});
Another way to use axios is to use the implicit methods like this:
const response = await axios.get(`http://localhost:8080/Uploadedfiles/${songName}`);
For a complete list of examples on different ways to use axios here is their official docs axios

sending more data along with formData to an API with axios

I´m writing an API that loads files in a folder
In order to select the files I’m using FormData. I use
<input type='file' ref={inputElement} onChange={handleChange} />
After choosing the file, In use axios to make a request
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
axios
.post('http://localhost:3001/upload', formData, {…
The api receives it and does it’s thing
app.post('/upload', (req, res) => {
if (!req.files) {
return res.status(500).send({ msg: 'file not specified' });
}
// accessing the file
const myFile = req.files.file;
It works fine.
But, I’d like to send extra info to the endpoint, so, I send the extra info and the formdata to axios:
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
axios
.post('http://localhost:3001/upload', {data: formData, extraInfo: 'more info'}, {
And in the endpoint I write:
app.post('/upload', (req, res) => {
console.log(req.body.extraInfo)
console.log(req.body.data)
extraInfo 'more info', ok, but data is empty, I supposed that data should contain formdata, but it’s empty, what can I do in order to get the formData and the extraInfo at the same time
Thanks in advance
Rafael
Just add the extraInfo to the formData and then send it to your server. You may need to double check how your server wants to get the data.
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
formData.append('extraInfo', "Some Info");// additonal data
axios
.post('http://localhost:3001/upload', formData, {

how to send images through axios using gridfs?

how can i send a file/image in reactjs uploaded through an to the backend, using axios?
the simple input form is this :
<form className={classes.form} onSubmit={handleSubmit(submitFunc)}>
<Grid container spacing={2}>
<Grid item xs={12}>
<input
type="file"
accept="image/*"
alt="file"
name="file"
id="file"
/>
</Grid>
</Grid>
<Button
type="submit"
>
Add
</Button>
</form>
the submit function:
try {
await axios
.post(`http://localhost:5000/addPic`, data, {
headers: {
accept: "application/json",
"Accept-Language": "en-US,en;q=0.8",
"Content-Type": `multipart/form-data`,
},
})
}
);
} catch (error) {
console.log("error");
}
i tried this but it's not working, and i dont know why, because when i use postman to send an image to the same api, it works :
also when i use a view engine and use a form with method="POST" in the backend, it works!
here's the api code :
const conn = mongoose.createConnection(mongoURI);
let gfs;
conn.once("open", () => {
// Init stream
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection("uploads");
});
let tempNameFile;
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "uploads",
};
tempNameFile = filename;
console.log(tempNameFile);
resolve(fileInfo);
});
});
},
});
const upload = multer({ storage });
router.post("/", upload.single("file"), async (req, res) => {
console.log(tempNameFile);
res.send("good to go");
});
to sum up all of this, my question is how can i upload an image to the frontend using a simple input and send it through axios, the same way postman sends an image to the backend to be handled by gridfs and stored in the mongodb database
Since Postman worked, your backend is setup properly. Now on to your frontend.
Axios handles multipart form data if your data is an instance of FormData.
In your component you can set a state variable to hold the selected file
const [selectedFile, setSelectedFile] = useState(null);
Add an onInput to your <input /> field like so:
<input onInput={e => setSelectedFile(e.target.files[0])} />
In the submit function, wrap the file in a FormData and submit with Axios
try {
const data = new FormData();
data.append("file", selectedFile);
await axios.post(`http://localhost:5000/addPic`, data);
/* Do something if upload was successful */
} catch (error) {
console.log("error");
}

Resources