Passing files from react-dropzone to express server - reactjs

I'm currently trying to send my files to my express server but every time I try and send my request the body is empty. After some research, it seems like they would not be stored in req.body so I'm not sure how to actually access the files that I've stored in my formdata.
Clientside code
export default async function UploadToFireStorage(accepetedfiles,fileVals){//Index are 1-1
const formData = new FormData();
formData.append('files', accepetedfiles);
try{
const response = await fetch(`https://someUrl`,{
method:"POST",
body: JSON.stringify(formData)
})
const jsonData = await response.json()
console.log(jsonData) //Attempt to see how my req is formed
}catch(error){
console.log(error)
}
}
express code
app.post('/uploadNewFiles',async(req,res)=>{
try{
const files = req.? // im not sure what to access after sending my formdata
for(let file = 0;file < files.length;file++){
let storageRef = ref(storage, `files/${files[file].path}`);
let uploadTask = await uploadBytesResumable(storageRef, files[file]);
}
res.json(files)
}catch(error){
res.json('Error: ' + error)
}
})
So just to clarify my question. I know when I make a fetch request I can package data that needs to be sent so I can post it and then extract it using something like let someVar = req.body.someVar and I've tried that with my file array but am unable to get my file array back and i'm not totally sure how to access it from req.

To properly post the files, don't use JSON.stringify. Set the body equal to formData directly. See this answer for more detail.
On the server side, you'll need to use a body parser that can handle multi-part form data, like multer . See this answer for more detail.

Related

How to upload a file from React front end to FastAPI?

as the title says, I'm trying to upload a file from React front end to FastAPI. The code I used is below:
//this is backend FastAPI ==================
#app.post("/uploadfile")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
//frontend ===================================
const [file, uploadFile] = useState(null)
//when upload button clicked
function handleSubmit(){
console.log(file[0].name)
const formdata = new FormData();
formdata.append(
"file",
file[0],
)
axios.post("/uploadfile", {
file:formdata}, {
"Content-Type": "multipart/form-data",
})
.then(function (response) {
console.log(response); //"dear user, please check etc..."
});
}
// this is when file has been selected
function handleChange(e){
uploadFile(e.target.files); //store uploaded file in "file" variable with useState
}
It returns a 422 (Unprocessable Entity). The message detail from axios is:
I am not quite familiar with the rules and format needed behind file uploading. Could someone clear my confusion?
Update from OP:
I have managed to solve the problem by replacing the axios part with
const headers={'Content-Type': file[0].type}
await axios.post("/uploadfile",formdata,headers)
.then()//etc
if anyone want to add more information on why that works please feel free to do so - since I'm not quite sure either.

How to use Axios.post() in a post request containing a json object request-body and a multipart form data (MP4)

Hi I Was wondering how I can send a a single axios post request containing a json object as the request body and also multipart form data (Mp4 file).
In my example I want to send 'details' and 'file'. I have tried sending details and file as 2nd and 3rd arguments to the axios.post() method but from what I can tell axios.post only accepts 2 args.
I have also tried appending the details and then the file, to the form data, but this does not work either.
If I split these into 2 seperate post calls, it works fine, but my application requires these to happen together.
I am getting the following error in my spring console:
[org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=----WebKitFormBoundaryqKyZ0R2SyFeDNCVp;charset=UTF-8' not supported]
Here is the error in my web dev tools console:
xhr.js:210 POST http://localhost:9191/api/123/file/upload 415
Id really appreciate any suggestions
const FileUpload = () => {
const [file, setFile]= useState(null)
const[details, setDetails] = useState({consent:false,
idConfirmed:false,
label:"",
roundId:""})
const changeHandler=(e)=>{
setFile(e.target.files[0]);
setDetails(prevDetails=>({
...prevDetails,
consent:true,
idConfirmed:true,
label:"test_Label"
}));
};
const handleSubmission=(e)=>{
e.preventDefault();
const data = new FormData();
data.append("file", file)
data.append("file", details)
console.log("Data: ", data)
axios.post(`${process.env.REACT_APP_URL_NEW_ROUND_VID}/123/file/upload`, data,
{
headers:{
"Content-Type":"multipart/form-data"
}
})
.then(res=>{
console.log("Data: ",res.data)
console.log("success")
})
.catch((e)=>{
console.log("Error", e)
})
//})
};
Here is my rest end point in Springboot:
#PostMapping(
path = "{patientId}/file/upload",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public void addWardRound(#PathVariable("patientId") String patientId,
#RequestParam("file") MultipartFile file,
#RequestBody WardRequest wardRequest){
WardRoundService.isFileEmpty(file);
WardRound round = service.saveRound(wardRequest);
String roundId = round.getRoundId();
service.uploadVid(patientId, roundId, file);
}
you can stringify and send the JSON data. but by using this method you need to parse it in the server!. it could be difficult
The best method is splitting it into 2 APIs
you can merge both API requests in UI by merging both APIs using promise.All
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
const promise1 = axios.get(URL1);
const promise2 = axios.post(URL2, data);
Promise.all([promise1, promise2]).then(function(values) {
console.log(values);
});
or else if you need to use the result of first API
then call the second API inside the response of first API itself

Spring API request giving "Content type 'application/octet-stream' not supported" error, however request is successful when using Postman

I am trying to send an API request through my React frontend to Spring backend. When I send the request I get this error:
Could not resolve parameter [0] in private org.springframework.http.ResponseEntity com.example.bottlecap.controllers.BottlecapController.entryForm(com.example.bottlecap.domian.Bottlecap,org.springframework.web.multipart.MultipartFile): Content type 'application/octet-stream' not supported
However, when I set up the request using Postman it goes through fine. I presume there may be an issue with how I am setting up my FormData on the React end. However, I have had little luck figuring it out.
My API is supposed to recieve an object that holds data about my submission as well as an image that goes with the submission. In my Postman request, I am creating a form data that holds a JSON file that holds all the object data and a random image just for testing. As I said, the requets goes through fine with this. However, in the frontend code, I am parsing through the object data as Json and adding it to a FormData as well as adding the image to the FormData.
Here is my Spring Controller:
#RequestMapping(path ="/bottlecap/entry", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE})
private ResponseEntity entryForm(#RequestPart("cap") Bottlecap cap, #RequestPart("file") MultipartFile image){
System.out.println(cap);
cap.toString();
System.out.println("Test");
return ResponseEntity.ok().build();
}
Here is my react Frontend form submission handler:
handleSubmit = event =>{
console.log(this.state.page);
console.log(this.state.page);
event.preventDefault();
const cap ={
"name":this.state.name,
"brand":this.state.brand,
"yearMade":parseInt(this.state.yearMade),
"country": this.state.country,
"description":this.state.description,
"yearFound":parseInt(this.state.yearFound),
"isAlcoholic":"true"
};
const stringCap = JSON.stringify({cap});
console.log(cap);
var formData = new FormData();
formData.append('cap', JSON.parse(stringCap));
formData.append('file',this.state.imageFile)
axios.post('http://localhost:8080/bottlecap/entry', formData, {headers:{'Content-Type':'multipart/form-data'}})
.then(res=>{
console.log(res);
console.log(res.data);
//window.location = "/success"
this.setState({pageDone:true})
this.setState({pageLoading:true})
})
}
Here is a screenshot of my Postman request if it may help.
Also here is the contents of the json file I am sending through on Postman, if it may help as well.
{"name":"post-test",
"brand":"post-test",
"yearMade":1000,
"country":"post-test",
"description":"post-test",
"yearFound":1000,
"isAlcoholic":"true"}
The last change I did was adding a header to the axios API request, but still no luck.
In postman, for parameter named cap, you're sending a .json file. But in your reactjs code, you're doing
formData.append('cap', JSON.parse(stringCap));
JSON.parse will create a javascript object which is not what your backend is expecting. You need to send it as a JSON file.
Not tested, but this might give you the idea.
const json = JSON.stringify(cap);
const blob = new Blob([json], {
type: 'application/json'
});
var formData = new FormData();
formData.append('cap', blob);
formData.append('file', this.state.imageFile)
axios.post('http://localhost:8080/bottlecap/entry', formData, {headers:{'Content-Type':'multipart/form-data'}})
.then(res=>{
console.log(res.data);
}
This is my fetch sample in Vue3, and it works thanks to you. Thanks!
let formData = new FormData();
formData.append('productJsonData', new Blob([JSON.stringify(productJsonObject)], {type: 'application/json'}));
formData.append('file', image); // This image comes from an <v-file-input> TAG
const response = await fetch(
`http://.../addProduct`,
{
headers: { 'Accept': 'application/json' },
method: 'POST',
body: formData
}
);
const responseData = await response.json();
if (!response.ok) {
console.log('REST error: [' + responseData.error + ']')
throw error;
}

upload image to S3 presigned url using react-native-image-picker and axios

I am trying to get an presigned url image upload working correctly. Currently the upload succeeds when selecting an image from the IOS simulator, however when I actually try to view the file it seems the file is corrupted and will not open as an image. I suspect it has something to do with my FormData but not sure.
export async function receiptUpload(file) {
const date = new Date();
const headers = await getAWSHeaders();
const presignUrl = await request.post(
urls.fileUpload.presignUpload,
{file_name: `${date.getTime()}.jpg`},
{headers}
)
.then(res => res.data);
const formData = new FormData();
formData.append('file', {
name: `${date.getTime()}.jpg`,
uri: file.uri,
type: file.type
});
const fileUpload = presignUrl.presignUrl && await request.put(
presignUrl.presignUrl,
formData
)
.then(res => res.status === 200);
}
I have tried from other fixes to change the file uri like so...
Platform.OS === 'android' ? file.uri : file.uri.replace('file://', '');
however this does not seem to work either.
I did this just recently in my current project and the following code is a working example for my use case. I didn't need to convert to a blob either though I am uploading to AWS S3 so if you are uploading elsewhere that may be the issue.
export const uploadMedia = async (fileData, s3Data, setUploadProgress = () => {}) => {
let sendData = { ...fileData };
sendData.data.type = sendData.type;
let formData = new FormData();
formData.append('key', s3Data.s3Key);
formData.append('Content-Type', fileData.type);
formData.append('AWSAccessKeyId', s3Data.awsAccessKey);
formData.append('acl', 'public-read');
formData.append('policy', s3Data.s3Policy);
formData.append('signature', s3Data.s3Signature);
formData.append('file', sendData.data);
return axios({
method: 'POST',
url: `https://${s3Data.s3Bucket}.s3.amazonaws.com/`,
data: formData,
onUploadProgress: progressEvent => {
let percentCompleted = Math.floor((progressEvent.loaded * 100) / progressEvent.total)
setUploadProgress(percentCompleted);
}
})
}
I would first check to see where the issue is occurring. After uploading can you view it on whatever storage service you are trying to upload it to. If so it's something on React Native side. If it doesn't ever get uploaded to the location you know its an error in your upload process. Might help you track the exact location of the error.
I had to do this recently for a project. I believe the data is a base64 string when coming directly from the file input. So the issue is your are uploading a base64 string not the image by simply passing the data field. I had to process it before uploading to the signed URL with the following method.
private dataUriToBlob(dataUri) {
const binary = atob(dataUri.split(',')[1]);
const array = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], { type: 'image/jpeg' });
}
This answer fixed it for me: How can I upload image directly on Amazon S3 in React Native?
I had tried uploading with axios and fetch with FormData. The download went through but the image file was not readable, even when downloaded to my Mac from the S3 console:
The file "yourfile.jpg" could not be opened. It may be damaged or use a file format that Preview doesn’t recognize.
Only after trying to upload with XHR with the correct Content-Type header did it work. Your signedUrl should be correct as well, which seems to be the case if the download goes through.

How to send image to server with Blob type using Axios in React Native?

I try to select a picture from the gallery, and I got data like below:
{
"exif":null,
"localIdentifier":"9F983DBA-EC35-42B8-8773-B597CF782EDD/L0/001",
"filename":"IMG_0003.JPG",
"width":500,
"modificationDate":null,
"mime":"image/jpeg",
"sourceURL":"file:///Users/vichit/Library/Developer/CoreSimulator/Devices/3BBFABAC-2171-49AA-8B2B-8C2764949258/data/Media/DCIM/100APPLE/IMG_0003.JPG",
"height":500,
"creationDate":"1344451932"
}
For this time, I want to send this picture to the server with Blob type using Axios.
I don't know how to convert this picture to a Blob type.
It's simple:
async function uploadToServer(sourceUrl) {
// first get our hands on the local file
const localFile = await fetch(sourceUrl);
// then create a blob out of it (only works with RN 0.54 and above)
const fileBlob = await localFile.blob();
// then send this blob to filestack
const serverRes = await fetch('https://www.yourAwesomeServer.com/api/send/file', { // Your POST endpoint
method: 'POST',
headers: {
'Content-Type': fileBlob && fileBlob.type,
},
body: fileBlob, // This is your file object
});
const serverJsonResponse = await serverRes.json();
// yay, let's print the result
console.log(`Server said: ${JSON.stringify(serverJsonResponse)}`);
}
And run like uploadToServer("file:///Users/vichit/Library/Developer/CoreSimulator/Devices/3BBFABAC-2171-49AA-8B2B-8C2764949258/data/Media/DCIM/100APPLE/IMG_0003.JPG")
It took me forever to figure it out, but it makes sense now.
I hope that helps someone!

Resources