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");
}
Related
I'm working on a React project with Facebook user integration.
I need to post a client-side generated image to a private group on behalf of the logged-in user.
Using the {group-id}/photo endpoint I can successfully post a picture which already exists on the web providing the url and caption parameters:
const postImageUrlToFacebook = () => {
const url = "https://upload.wikimedia.org/wikipedia/commons/e/e0/New.gif";
let caption = "test upload url image from React";
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", { url, caption })
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
};
The definition of the httpFacebookGraphClient is the following:
import axios, { AxiosRequestConfig } from "axios";
const httpFacebookGraphClient = (token: string | null) => {
const defaultOptions = {
baseURL: "https://graph.facebook.com/v14.0",
method: "get",
// withCredentials: true,
headers: {
"Content-Type": "application/json",
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the access token parameter for any request
instance.interceptors.request.use((config: AxiosRequestConfig): AxiosRequestConfig => {
if (!config) {
config = {};
}
if (!config.params) {
config.params = {};
}
config.params.access_token = token;
config.params.limit = "999";
return config;
});
return instance;
};
export default httpFacebookGraphClient;
I would now need to start from a default svg, modifiy some g tags inside of it via javascript, convert to jpg and post it to a facebook group.
Let's suppose the starting SVG is the following:
<svg id="Livello_1" data-name="Livello 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 107"><defs><style>.cls-1,.cls-3,.cls-4,.cls-5{fill:#ffc000;}.cls-2{fill:#fff;}.cls-3,.cls-4,.cls-5{stroke:#fff;stroke-miterlimit:10;}.cls-3{stroke-width:0.87px;}.cls-4{stroke-width:0.79px;}.cls-5{stroke-width:0.65px;}</style></defs><title>bannerino scegli il tuo sconto</title><path class="cls-1" d="M136.88,2.63a10,10,0,0,1,10,10V94.37a10,10,0,0,1-10,10H13.13a10,10,0,0,1-10-10V12.63a10,10,0,0,1,10-10H136.88m0-2H13.13a12,12,0,0,0-12,12V94.37a12,12,0,0,0,12,12H136.88a12,12,0,0,0,12-12V12.63a12,12,0,0,0-12-12h0Z"/></svg>
I started from the end, trying to post a local image to the Facebook group before trying to build the image, but I'm already stuck.
Reading the Facebook api docs at this link I found this sentence on the url parameter;
The URL of a photo that is already uploaded to the Internet. You must specify this or a file attachment
mentioning a file attachment.
Again on the Facebook docs I found this that explain how to upload a file to Facebook to use it in subsequent api calls, but I can't make it to work.
Anyone could give me a hint on how to proceed?
I found the way around this and I can successfully post an image selected by input field client side.
React component state:
const [fileToUpload, setFileToUpload] = useState<any>();
inputFileChangeHandler:
const inputFileChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.currentTarget != null && event.currentTarget.files != null) {
setFileToUpload(event.currentTarget.files[0]);
}
};
uploadImageToFacebook:
const uploadImageToFacebook = () => {
let caption = "test upload local image from React";
const fileReader = new FileReader();
fileReader.onloadend = async () => {
if (fileReader.result != null) {
const photoData = new Blob([fileReader.result], { type: "image/png" });
const formData = new FormData();
formData.append("source", photoData);
formData.append("caption", caption);
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
};
fileReader.readAsArrayBuffer(fileToUpload);
};
On jsx:
<input type="file" id="file-input" onChange={inputFileChangeHandler} />
<button onClick={uploadImageToFacebook}>Invia</button>
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
I am trying to implement wopi protocol.
Using react, I have an iframe and a post-form which sets the content of iframe identified as collabora-online-viewer by requesting to address props.url with token props.token, as below (which works correctly):
useEffect(() => {
formElem.current.submit();
});
return (
<div style={{display: 'none'}}>
<form
ref={formElem}
action={props.url}
encType="multipart/form-data"
method="post"
target="collabora-online-viewer"
id="collabora-submit-form"
>
<input
name="access_token"
value={props.token}
type="hidden"
id="access-token"
/>
<input type="submit" value="" />
</form>
</div>
);
// my iframe in another component
<iframe
ref={iframeRef}
title="Collabora Online Viewer"
id="collabora-online-viewer"
name="collabora-online-viewer"
/>
Instead of sending this request by submitting a post-form, I need this request sent by my own express server using axios. How can i do this?
I have done as below, but it did not work:
//CLIENT SIDE
useEffect(() => {
sendRequest();
}, []);
async function sendRequest() {
const obj = {
url: props.url,
token: props.token
};
await axios
.post('/MyRequest', obj)
.then((res) => {
props.setIframeContent(res.data);
})
.catch((error) => {
console.log(error);
});
}
// my iframe in another component
const [iframeContent, setIframeContent] = useState('');
<iframe
srcDoc= {iframeContent}
ref={iframeRef}
title="Collabora Online Viewer"
id="collabora-online-viewer"
name="collabora-online-viewer"
/>
//SERVER SIDE
routeAuthenticated(
'POST',
'/MyRequest',
async (req, res) => {
try {
await axios
.post(req.body.url, req.body.token, {
// headers: {'Content-Type': 'multipart/form-data'}
})
.then((response) => {
res.send(response.data);
});
} catch (err) {
console.log(err);
}
}
);
Unfortunately it's not possible to post to Office Online Server with JavaScript. When an html form is submitted directly e.g.
<form action="https://someotherorigin.com">
Then no JavaScript is used, so CORS does not come into play when you post to OOS. But axios (or fetch or superagent etc.) use JavaScript, so CORS will block the request. See this SO post for more detail.
Hello I am trying to upload images from my react frontend but its unable to read the file in axios and the images are not being uploaded.
This is my code.
<form onSubmit={Upload}>
<input
type='file'
onChange={(e) => {
generate(e.target.files);
}}
/>
<button type='submit'>Submit</button>
</form>
const generate = async (data) => {
const payload = {
entity: 'test',
userId: '200',
documentFileName: data[0].name,
action: 'putObject',
};
console.log('DAATA', data[0].name);
await api
.generatePreSignedUrl(payload, token)
.then((res) => {
setUploadUrl(res.data.data);
setImageData(data);
})
.catch((err) => {
console.log('Error', err);
});
};
const Upload = async (e) => {
e.preventDefault();
console.log('IMAGEdata', imageData);
console.log('URL', uploadUrl);
let file = imageData[0];
console.log('THIS IS FILE', file);
console.log('FILENAME', file.name);
console.log('FIELETYPE', file.type);
var options = {
headers: {
'Content-Type': file.type,
},
};
await axios
.put(uploadUrl, file, options)
.then((res) => {
console.log('Response from s3');
console.log(res);
})
.catch((error) => {
alert('ERROR ' + JSON.stringify(error));
});
};
Here imageUrl is the signed URL that I get from was to upload my files. I am successfully able to call the put request to upload the file but the image or file is not being read and uploaded.
This is the response when I am trying to look at the uploaded file.
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
<Key>documents/ama/test/200/displaypicture.png</Key>
<RequestId>R7M63AHQKDNC6NH5</RequestId>
<HostId>3u8Jb8Ev4S1s/ODicL3Py4hvrvwKKOs/5zGxUEDYORfN1+U37Zx8PfYvIcGWUIAfeCqHpWqXd8o=</HostId>
</Error>
Look at the AWS SDK for JavaScript Developer Guide Version 3. There are examples that show how to upload photos to an Amazon S3 bucket.
Uploading photos to Amazon S3 from a browser
A pretty simple way to use this node module to achieve this:
https://www.npmjs.com/package/react-aws-s3
https://medium.com/#steven_creates/uploading-files-to-s3-using-react-js-hooks-react-aws-s3-c4c0684f38b3
enter image description here
npm install --save react-aws-s3
User " aws-sdk " and use this code
import React ,{useState} from 'react';
import AWS from 'aws-sdk'
const S3_BUCKET ='YOUR_BUCKET_NAME_HERE';
const REGION ='YOUR_DESIRED_REGION_HERE';
AWS.config.update({
accessKeyId: 'YOUR_ACCESS_KEY_HERE',
secretAccessKey: 'YOUR_SECRET_ACCESS_KEY_HERE'
})
const myBucket = new AWS.S3({
params: { Bucket: S3_BUCKET},
region: REGION,
})
const UploadImageToS3WithNativeSdk = () => {
const [progress , setProgress] = useState(0);
const [selectedFile, setSelectedFile] = useState(null);
const handleFileInput = (e) => {
setSelectedFile(e.target.files[0]);
}
const uploadFile = (file) => {
const params = {
ACL: 'public-read',
Body: file,
Bucket: S3_BUCKET,
Key: file.name
};
myBucket.putObject(params)
.on('httpUploadProgress', (evt) => {
setProgress(Math.round((evt.loaded / evt.total) * 100))
})
.send((err) => {
if (err) console.log(err)
})
}
return <div>
<div>Native SDK File Upload Progress is {progress}%</div>
<input type="file" onChange={handleFileInput}/>
<button onClick={() => uploadFile(selectedFile)}> Upload to S3</button>
</div>
}
export default UploadImageToS3WithNativeSdk;
When I test sending a request containing both image and text grabbbed from user, it comes through to the backend with proper data when I use Postman. Not from React front-end, though. Request does come through but req.body seems to be empty when I console.log it from backend. What am I doing wrong? I am using Multer.
//FRONT-END
import React, { useState } from 'react';
import axios from 'axios';
const ListProperty = (props) => {
const [address, setAddress] = useState('');
const [file, setFile] = useState(null);
const [filename, setFilename] = useState('Choose File');
const handleAddressChange = (evt) => {
setAddress(evt.target.value);
};
const handlePhotoSelect = (evt) => {
setFile(evt.target.files[0]);
setFilename(evt.target.files[0].name);
};
const handleSubmit = async (evt) => {
evt.preventDefault();
const formData = new FormData();
formData.append('address', address);
formData.append('upload', file);
console.log(formData);
try {
axios.post('http://localhost:3000/listproperty', {
headers: { 'Content-Type': 'multipart/form-data' },
body: formData,
});
} catch (err) {
console.log(err);
}
};
return (
<div>
<h2>Property Listing Form</h2>
<span>Provide property address and Photo</span>
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<button>Click to list</button>
</form>
</div>
);
};
export default ListProperty;
//BACK-END
const express = require('express');
const PropertyModel = require('../models/propertyModel');
const router = new express.Router();
const UserModel = require('../models/userModel');
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/images');
},
filename: function (req, file, cb) {
const uniqueName = `${Math.random().toString(32).slice(2)}.jpg`;
req.image = uniqueName;
cb(null, uniqueName);
},
});
const upload = multer({ storage });
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
console.log('hitting Backend router');
const property = new PropertyModel({
...req.body,
owner: req.user._id,
photo: req.image,
});
await UserModel.findByIdAndUpdate(req.user._id, {
$push: { properties: property._id },
});
try {
await property.save();
res.status(200).send(property);
} catch (err) {
console.log(err);
res.status(400).send(err);
}
}
);
module.exports = router;
If you are sending form data in the body you need to use the formidable npm module
you can install it using npm i formidable
then require formidable at top of the file
var formidable = require("formidable");
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
var form = new formidable.IncomingForm();
form.multiples = false;
form.parse(req, async function (err, fields, files) {
/**now here you can get all files in files and fields with fields
in your case you have sent
formData.append('address', address);
formData.append('upload', file);
above two data in form
so you can get your image from files.upload
and address fields.address **/
})
})
In addition, I would suggest you use Axios for api calls
your axios request is not right. axios post request accepts data as a second argument and third argument is for options ( headers etc ),
axios.post('http://localhost:3000/listproperty', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
another thing is your request is not being triggered at all. try setting input type to submit instead of using the button to trigger onSubmit handler of the form.
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<input type="submit" value="Submit" />
</form>