React Api Call with a File as Parameter - javascript

I have this handler to make api calls to an endpoint:
handleFileChange(e) {
e.preventDefault();
fetch(apiAddress)
.then((res) => {
return res.json()
}).then( (res) => {
this.props.onFileChange(JSON.stringify(res));
});
}
And i would like send a file as part of the requests like this:
render(){
getScene();
return (
<form>
<input type='file' name='uploaded_file' />
<button onClick={this.handleFileChange}>Upload</button>
</form>
)
}
How can it do that with the file added in that form?

This is just as simple as POSTing the File to the API:
handleFileChange(e) {
e.preventDefault();
let fileInput = document.getElementsByName("uploaded_file")[0];
fetch('/yourEndpoint', {
method: 'POST'
body: fileInput.files[0] // This is your file object
headers: {
"Content-Type": fileInput.files[0].type // this is the MIME type of the data *
},
}).then(
response => response.json()
).then( res => {
this.props.onFileChange(JSON.stringify(res))
});
}
* note, however, that this is generated from the file extension, so it can easily be spoofed

Related

How can I download a zipped folder from a post API in REACT

I have a task to implement a download for files in excel format and images contained in a folder as a zip file. The API is a post but I use the JSzip library and I've not being able to get a proper implementation.
import { saveAs } from "file-saver";
import JSZip from "jszip";
function Download({ apiToken }) {
const [category, setCategory] = useState([]);
//click to download function
const handleClick = () => {
setButtonText("Downloading...");
const params = JSON.stringify({
subcategoryiid: category,
});
Axios({
url: `${baseUrl}/cards/details`,
method: "POST",
responseType: "blob",
headers: {
Authorization: `Bearer ${apiToken}`,
Accept: "application/json",
"Content-Type": "application/json",
},
body: params,
})
.then((res) => {
console.log(res);
let zip = new JSZip();
let zipData = res.data;
zip.folder("project.zip", zipData);
zip.generateAsync({ type: "blob" }).then((content) => {
// Force down of the Zip file
saveAs(content, "project.zip");
});
})
.catch((err) => {
console.log(err);
setButtonText("Download");
});
};
return (
<div>
<TextField
className="box"
placeholder="enter facility code"
required
value={category}
onChange={(e) => setCatergory(e.target.value)}
/>
<Button
type="button"
color="success"
variant="contained"
onClick={handleClick}
>
{buttonText}
</Button>
</Paper>
</div>
);
}
export default Download;
I want to be able to read the file and download the folder(containing different files and images) as zip for the user to extract. The zip comes in empty whereas in the browser console it contains some files. I would appreciate any help please
This how I solve this case.
My Service
async download(appName: string): Promise<any> {
return fetch(`${this.URL}/download/${appName}`)
.then(transfer => transfer.blob())
.then(bytes => {
let elm = document.createElement('a');
elm.href = URL.createObjectURL(bytes);
elm.setAttribute('download', `${appName}.zip`);
elm.click()
return true;
})
.catch((error) => {
console.log(error);
})
}
How I call it
const onDownload = async () => {
setLoading(true);
const resp = await generateService.download(app.projectName);
if (resp) {
setLoading(false);
}
}

jQuery ajax call is returning response data as String instead of Object

On the following StackBlitz:
https://stackblitz.com/edit/react-jbthdw?file=src%2FApp.js
I have a code which fetches a JSON data with a list of names.
There are two ways of returning data: Axios and jQuery.
With the Axios approach the code works properly.
Now, because some business decisions I need to replace Axios with jQuery.
My problem is: with jQuery the response.data is fetched as string when it should be as object as it happens with Axios.
Below you have the code, but feel free to play with the StackBlitz playground I provided above:
import React from 'react';
import axios from 'axios';
import $ from 'jquery';
import './style.css';
export default () => {
const jsonSource = 'https://raw.githubusercontent.com/tlg-265/mockend/master/data.json';
const handleClickAxios = () => {
axios.get(jsonSource).then(response => {
console.log({ responseType: (typeof response.data)});
console.log(response.data);
});
};
const handleClickJQuery = () => {
sendApiRequest({ url: jsonSource, method: 'get' }).then(response => {
console.log({ responseType: (typeof response.data)});
console.log(response.data);
});
};
return (
<div>
<h1>Axios vs jQuery</h1>
<p><b>Inconsitency:</b> returning response as Object vs String</p>
<button onClick={handleClickAxios}>Axios</button>{' '}
<button onClick={handleClickJQuery}>jQuery</button>
</div>
);
};
export const sendApiRequest = ({
url,
method,
data,
timeout,
}) => {
method = method.toUpperCase();
let additionalSettings = { data: data };
if (method === 'POST') {
additionalSettings = {
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(data),
};
}
return new Promise((resolve, reject) => {
$.ajax({
url,
method: method,
timeout,
...additionalSettings,
success: (response) => {
resolve({
data: response
});
},
error: ({ responseJSON }) => {
reject(responseJSON);
},
});
});
};
Here you have a screenshot of the issue:
Any idea on what do I need to update on the code in order to get: response.data as object with jQuery? I need to get that without any post processing of the response. Is there maybe any params I could use with jQuery so it behaves similarly to Axios?
Thanks!
The URL you are requesting includes this response header:
content-type: text/plain; charset=utf-8
Which is why jQuery isn't processing it as JSON automatically.
A quick search of the documentation for the word "json" quickly brings up the dataType option which lets you override the content-type.
const jsonSource = 'https://raw.githubusercontent.com/tlg-265/mockend/master/data.json';
jQuery.ajax({
url: jsonSource,
dataType: 'json'
}).then(data => {
console.log({
responseType: (typeof data)
});
console.log(data);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Why am I getting a 500 when uploading file via the browser but not via Postman? [duplicate]

Using raw HTML when I post a file to a flask server using the following I can access files from the flask request global:
<form id="uploadForm" action='upload_file' role="form" method="post" enctype=multipart/form-data>
<input type="file" id="file" name="file">
<input type=submit value=Upload>
</form>
In flask:
def post(self):
if 'file' in request.files:
....
When I try to do the same with Axios the flask request global is empty:
<form id="uploadForm" enctype="multipart/form-data" v-on:change="uploadFile">
<input type="file" id="file" name="file">
</form>
uploadFile: function (event) {
const file = event.target.files[0]
axios.post('upload_file', file, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
If I use the same uploadFile function above but remove the headers json from the axios.post method I get in the form key of my flask request object a csv list of string values (file is a .csv).
How can I get a file object sent via axios?
Add the file to a formData object, and set the Content-Type header to multipart/form-data.
var formData = new FormData();
var imagefile = document.querySelector('#file');
formData.append("image", imagefile.files[0]);
axios.post('upload_file', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
Sample application using Vue. Requires a backend server running on localhost to process the request:
var app = new Vue({
el: "#app",
data: {
file: ''
},
methods: {
submitFile() {
let formData = new FormData();
formData.append('file', this.file);
console.log('>> formData >> ', formData);
// You should have a server side REST API
axios.post('http://localhost:8080/restapi/fileupload',
formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function () {
console.log('SUCCESS!!');
})
.catch(function () {
console.log('FAILURE!!');
});
},
handleFileUpload() {
this.file = this.$refs.file.files[0];
console.log('>>>> 1st element in files array >>>> ', this.file);
}
}
});
https://codepen.io/pmarimuthu/pen/MqqaOE
If you don't want to use a FormData object (e.g. your API takes specific content-type signatures and multipart/formdata isn't one of them) then you can do this instead:
uploadFile: function (event) {
const file = event.target.files[0]
axios.post('upload_file', file, {
headers: {
'Content-Type': file.type
}
})
}
Sharing my experience with React & HTML input
Define input field
<input type="file" onChange={onChange} accept ="image/*"/>
Define onChange listener
const onChange = (e) => {
let url = "https://<server-url>/api/upload";
let file = e.target.files[0];
uploadFile(url, file);
};
const uploadFile = (url, file) => {
let formData = new FormData();
formData.append("file", file);
axios.post(url, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
}).then((response) => {
fnSuccess(response);
}).catch((error) => {
fnFail(error);
});
};
const fnSuccess = (response) => {
//Add success handling
};
const fnFail = (error) => {
//Add failed handling
};
This works for me, I hope helps to someone.
var frm = $('#frm');
let formData = new FormData(frm[0]);
axios.post('your-url', formData)
.then(res => {
console.log({res});
}).catch(err => {
console.error({err});
});
this is my way:
var formData = new FormData(formElement);
// formData.append("image", imgFile.files[0]);
const res = await axios.post(
"link-handle",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
How to post file using an object in memory (like a JSON object):
import axios from 'axios';
import * as FormData from 'form-data'
async function sendData(jsonData){
// const payload = JSON.stringify({ hello: 'world'});
const payload = JSON.stringify(jsonData);
const bufferObject = Buffer.from(payload, 'utf-8');
const file = new FormData();
file.append('upload_file', bufferObject, "b.json");
const response = await axios.post(
lovelyURL,
file,
headers: file.getHeaders()
).toPromise();
console.log(response?.data);
}
There is an issue with Axios version 0.25.0 > to 0.27.2 where FormData object in a PUT request is not handled correctly if you have appended more than one field but is fine with one field containing a file, POST works fine.
Also Axios 0.25.0+ automatically sets the correct headers so there is no need to specify Content-Type.
For me the error was the actual parameter name in my controller... Took me a while to figure out, perhaps it will help someone. Im using Next.js / .Net 6
Client:
export const test = async (event: any) => {
const token = useAuthStore.getState().token;
console.log(event + 'the event')
if (token) {
const formData = new FormData();
formData.append("img", event);
const res = await axios.post(baseUrl + '/products/uploadproductimage', formData, {
headers: {
'Authorization': `bearer ${token}`
}
})
return res
}
return null
}
Server:
[HttpPost("uploadproductimage")]
public async Task<ActionResult> UploadProductImage([FromForm] IFormFile image)
{
return Ok();
}
Error here because server is expecting param "image" and not "img:
formData.append("img", event);
public async Task<ActionResult> UploadProductImage([FromForm] IFormFile image)

Calling Api post method on button click in React

I have an event like below:
handleDownload(e) {
e.preventDefault();
alert('Hi');
let communityName = this.state['selectedCommunity'];
let files = this.state[files];
fetch(clientConfiguration['filesApi.local'], {
method: 'POST',
headers: new Headers(),
body: JSON.stringify({ communityName: communityName, body: files })
}).then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.log(err))
};
I have a button as below:
renderDownloadButton() {
if (this.state.files && this.state.files.filter(i => i.checked).length) {
return (
<button id="download" styles="display: none;" onClick={this.handleDownload} >
Download
</button>
);
}
};
It fires but it is giving following error, any help please - thank you. At
let communityName = this.state['selectedCommunity'];
its giving me the error;
Can not read property state undefined
Any help please?
My guess is that you need to bind your handler, but it's really hard to tell without whole component code.
handleDownload = (e) => {
e.preventDefault();
alert('Hi');
let communityName = this.state['selectedCommunity'];
let files = this.state[files];
fetch(clientConfiguration['filesApi.local'], {
method: 'POST',
headers: new Headers(),
body: JSON.stringify({ communityName: communityName, body: files })
}).then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.log(err))
}
For the api post not fetching the error can be one possible case of CORS error that browser do not allow you to access other network(private and secured IP address) so you need to actually allow the proxy setting as I can see your post data don't have any proxy enabled. Here is the code I am attaching
This is the pseudo code please make changes accordingly in your fetch method:
var targetUrl ='/downloadableReport'
const res= fetch(targetUrl,{
method: 'POST',
headers: {
'Content-Type': "application/json; charset=utf-8",
},
body: JSON.stringify({
"requestData":{
"userName":"XXX",
"password":"XXXX",
"clientId":"XXXX",
"txnType":"XXXX"
}
})
})
.then(response => response.json())
.catch(error =>{
console.log(error)
})
Also, you need to add setupProxy.js file (mind the name of the file should be this only) and add this code (with preferred changes)
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
proxy("/downloadableReport",{
target:"http://192.168.1.220:8080/report/abc" ,
changeOrigin:true
})
)
};

antd upload api: how to return promise

I am working with antd framework and I have to use upload API.
This is the signature:
action: Uploading URL : string|(file) => Promise
I am invoking the API in this way trying to return a Promise:
<Upload {...propsUpload}>
<Button> <Icon type="upload" />Upload</Button>
</Upload>
with propsUpload that refers to function uploadMedia
const propsUpload = {
action: this.uploadMedia,
listType: 'picture',
defaultFileList: [],
className: 'upload-list-inline',
};
and this is the function uploadMedia
uploadMedia = (file) => {
let formData = new FormData();
formData.append('file', file);
formData.append('uuid', this.generateuuid());
formData.append('domain', 'POST');
formData.append('filename', file.name );
return fetch(process.env.REACT_APP_API_URL +
'/v100/media/upload', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json'
},
body: formData
})
.then(response => response.json())
.then(data => data.data)
.catch(error => {
console.log('Error fetching profile ' + error)
})
}
The file is uploaded to server correctly.
But after the call to API, antd try to do another call that fails, maybe because I am not returning the correct value from function.
As result the thumbnail is displayed with red border and and error is shownd. In the image below there are both (the call that fails and image with red border)
What type of object I have to return in function uploadMedia to use api correctly?
Thanks
I haven't used antd but looking at the docs of Uplaod component I think you're using it wrong. Look at the examples there and see the code, action expects either a URL or a Promise that will return this URl. And Upload in this case will make request itself, so you don't need to do fetch. And your promise returns the data (object) so the Upload sends the request to [object Object] (which is what's returned by .toString() when applied to an object in JS)
EDIT
Try to check all examples in docs, I can see that there is an example when you want to manually upload the file (if you really need it)
For anyone looking to access the response object after calling the API. There are two ways you can get access to the response.
Implement a custom API request mentioned as in other answers for this question.
Use the onChange method provided by AntD (Which is the easier than utilizing the custom request)
I will explain the second approach below using a code block.
const fileUploadProps = {
name: "file",
action: config.remote + "api/file",
method: "POST",
showUploadList: false,
headers: {
authorization: "authorization-text",
contentType: "multipart/form-data"
},
onChange(info) {
if (info.file.status === "done") {
const { response } = info.file;
updateProfile(response.payload.file);
} else if (info.file.status === "error") {
message.error("Error uploading the file");
props.endLoad();
}
},
beforeUpload(file) {
const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
if (!isJpgOrPng) {
message.error("You can only upload JPG/PNG file!");
}
const isLt2M = file.size / 1024 / 1024 < 2;
const isGT20K = file.size / 1024 > 20;
if (!isLt2M) {
message.error("Image must smaller than 2MB!");
}
if (!isGT20K) {
message.error("Image must larger than 20KB!");
}
if (isJpgOrPng && isLt2M && isGT20K) {
props.startLoad();
return true;
} else {
return false;
}
}
};
In Render function I have the AntD upload component
<Upload {...fileUploadProps}>
<Button icon={<CameraFilled style={{ fontSize: "30px" }} />}></Button>
</Upload>
You can notice how I got the access to the response object inside onChange function.
Once the upload is complete it will call the onChange function having response object inside the info object.
So from there you can get access to your data object easily and call the next method.
I solved using api customRequest in this way:
uploadMedia = (componentsData) => {
let formData = new FormData();
formData.append('file', componentsData.file);
formData.append('uuid', this.generateuuid());
formData.append('domain', 'POST');
formData.append('filename', componentsData.file.name );
fetch(process.env.REACT_APP_API_URL + '/v100/media/upload', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json'
},
body: formData
})
.then(response => response.json())
.then(data => data.data)
.then(data=> componentsData.onSuccess())
.catch(error => {
console.log('Error fetching profile ' + error)
componentsData.onError("Error uploading image")
})
}
For those who are not clear how to actually implement it (and it is unclear in docs):
Just implement a customRequest function in the props that accepts two callbacks, which are onError and onSuccess, and other data such as file and filename.
Like this
const props = {
customRequest: (componentsData) => {
let formData = new FormData();
formData.append('file', componentsData.file);
formData.append('uuid', this.generateuuid());
formData.append('domain', 'POST');
formData.append('filename', componentsData.file.name );
fetch(process.env.REACT_APP_API_URL + '/v100/media/upload', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json'
},
body: formData
})
.then(response => response.json())
.then(data => data.data)
.then(data=> componentsData.onSuccess())
.catch(error => {
console.log('Error fetching profile ' + error)
componentsData.onError("Error uploading image")
})
}
}
And let Upload component receive the props.
const App = () => {
return <Upload {...props} />
}

Categories

Resources