React image upload to FastAPI Unprocessable Entity - javascript

I have created an API using FastApi that processes the image uploaded and return the processed image.
Here is the image upload endpoint
#app.post("/predict")
async def root(file: UploadFile = File(...)):
..............................
res,im_png = cv2.imencode(".png", bg_img)
return StreamingResponse(io.BytesIO(im_png.tobytes()), media_type="image/png")
What I have done in the frontend:
class Detect extends Component {
state = {
title: '',
content: '',
image: null
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
};
handleImageChange = (e) => {
this.setState({
image: e.target.files[0]
})
};
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state);
let form_data = new FormData();
form_data.append('image', this.state.image, this.state.image.name);
let url = 'http://127.0.0.1:8000/predict';
axios.post(url, form_data, {
headers: {
'content-type': 'multipart/form-data'
}
})
.then(res => {
console.log(res.data);
})
.catch(err => console.log(err))
};
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit}>
<p>
<input type="file"
id="image"
accept="image/png, image/jpeg" onChange={this.handleImageChange} required/>
</p>
<input type="submit"/>
</form>
</div>
);
}
}
export default Detect;
When I upload the image through frontend and submit it the API shows "Unprocessable Entity", while it was working fine when I was using Swagger UI.
What I believe is the image is not being received by the FastApi as a type that it can processed
How can I resolve this issue??

You should add your image to the FormData object, using the same key defined in the endpoint for the file upload (in your case, that is, file). Thus, you should do:
form_data.append('file', this.state.image, this.state.image.name);

Related

Why is my server not receiving my uploaded file?

In Postman I can send files and they are received fine. My client [react] isn't managing to and my server always receives undefined.
Here is my handleSubmit function and form:
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("file", e.target.file.files[0]);
dispatch(upload(formData));
};
return (
// form for uploading a file
<form onSubmit={handleSubmit} encType="multipart/form-data">
<input type="file" name="file" />
<button type="submit">Upload</button>
</form>
);
And here is my axios call:
const API_URL = "/api/uploads/";
const upload = async (file) => {
const formData = new FormData();
formData.append("file", file);
const response = await axios.post(API_URL, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
withCredentials: true,
});
return response.data;
};
am I doing something wrong? I've been debugging for hours...

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

How to render Streamable image on React coming from FastAPI server?

I would like to render an image on React returned from FastAPI backend using StreamingResponse. The image is in the form of a numpy array, which is of cv2 type of object.
#app.post("/predict")
async def root(file: UploadFile = File(...)):
global model
global store_coordinates
global store_faces
global store_mesh
content = await file.read()
nparr = np.fromstring(content, np.uint8)
bg_img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
......................
for i in range(len(store_coordinates)):
x, y, w, h = store_coordinates[i]
bg_img [b:b + d, a:a + c] = store_mesh[i]
res,im_png = cv2.imencode(".png", bg_img)
return StreamingResponse(io.BytesIO(im_png.tobytes()), media_type="image/png")
Here, I have created an API endpoint in which the uploaded image is received using POST request, and a StreamableResponse(Image) is returned.
How can I render this returned response on React frontend?
React Code:
import React, { Component } from "react";
import axios from "axios";
class Detect extends Component {
state = {
title: "",
content: "",
image: null,
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value,
});
};
handleImageChange = (e) => {
this.setState({
image: e.target.files[0],
});
};
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state);
let form_data = new FormData();
form_data.append("image", this.state.image, this.state.image.name);
let url = "http://127.0.0.1:8000/predict";
axios
.post(url, form_data, {
headers: {
"content-type": "multipart/form-data",
},
})
.then((res) => {
console.log(res.data);
})
.catch((err) => console.log(err));
};
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit}>
<p>
<input
type="file"
id="image"
accept="image/png, image/jpeg"
onChange={this.handleImageChange}
required
/>
</p>
<input type="submit" />
</form>
<div id="image-render">
<img></img>
</div>
</div>
);
}
}
export default Detect;
I would like to render the returned image in the div tag which has the id of image-render.
Edit - This is the response I get from my backend.
You could either encode the image data to Base64 format on server side and return the base64-encoded string, which can then be used to display the image in the HTML page as shown here (e.g., <img src="data:image/png;base64, ...), or send the raw bytes—it might be best not to use a StreamingResponse for sending the raw bytes, as you currently do, but rather use a custom Response, as shown in this and this answer, as the entire image bytes are already loaded into memory—and, on client side, convert them into either a base64-encoded string (using btoa(), String.fromCharCode() and Uint8Array), or a Blob object (and then call URL.createObjectURL() to create a URL representing the Blob object). The examples below show how to implement the last two methods (i.e., convert the image to either a base64-encoded string or Blob object on client side), using Axios library (which you seem to be using in your project), as well as Fetch API.
Using Axios
Option 1 - Convert raw image bytes into Blob object
axios({
method: 'POST',
url: '/upload',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
},
responseType: "blob"
})
.then(response => {
var blobURL = URL.createObjectURL(response.data);
var image = document.getElementById("myImage");
image.onload = function(){
URL.revokeObjectURL(this.src); // release the blob URL once the image is loaded
}
image.src = blobURL;
})
.catch(error => {
console.error(error);
});
Option 2 - Convert raw image bytes into base64-encoded string
axios({
method: 'POST',
url: '/predict',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
},
responseType: "arraybuffer"
})
.then(response => {
base64string = btoa(String.fromCharCode(...new Uint8Array(response.data)))
contentType = response.headers['content-type']
return base64string;
})
.then(base64string => {
var image = document.getElementById("myImage");
image.src = "data:" + contentType + ";base64," + base64string;
})
.catch(error => {
console.error(error);
});
Remember to define an <img> tag in your HTML file, where you wish to display the image:
<img id="myImage" src="">
Using Fetch API
Option 1 - Convert raw image bytes into Blob object
fetch('/predict', {
method: 'POST',
body: formData,
})
.then(response => response.blob())
.then(blob => {
var blobURL = URL.createObjectURL(blob);
var image = document.getElementById("myImage");
image.onload = function(){
URL.revokeObjectURL(this.src); // release the blob URL once the image is loaded
}
image.src = blobURL;
})
.catch(error => {
console.error(error);
});
Option 2 - Convert raw image bytes into base64-encoded string
fetch('/predict', {
method: 'POST',
body: formData,
})
.then(response => {
contentType = response.headers.get('content-type')
return response.arrayBuffer();
})
.then(arrayBuffer => {
base64string = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)))
var image = document.getElementById("myImage");
image.src = "data:" + contentType + ";base64," + base64string;
})
.catch(error => {
console.error(error);
});
Remember to define an <img> tag in your HTML file, where you wish to display the image:
<img id="myImage" src="">

"Cannot POST /" in Vue (Gridsome) App - Netlify Forms prevent redirect

I am building a personal website with Gridsome. I am trying to set up a newsletter signup form via Netlify Forms. I don't want the user to be redirected after clicking 'Submit'. To prevent that I use #submit.prevent like so:
<form name= "add-subscriber" id="myForm" method="post" #submit.prevent="handleFormSubmit"
data-netlify="true" data-netlify-honeypot="bot-field">
<input type="hidden" name="form-name" value="add-subscriber" />
<input type="email" v-model="formData.userEmail" name="user_email" required="" id="id_user_email">
<button type="submit" name="button">Subscribe</button>
</form>
Then using a mix of the following guides (gridsome guide, CSS-Tricks guide) I do the following in my script section:
<script>
import axios from "axios";
export default {
data() {
return {
formData: {},
}
},
methods: {
encode(data) {
return Object.keys(data)
.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
.join('&')
},
handleFormSubmit(e) {
axios('/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: this.encode({
'form-name': e.target.getAttribute('name'),
...this.formData,
}),
})
.then(() => this.innerHTML = `<div class="form--success">Almost there! Check your inbox for a confirmation e-mail.</div>`)
.catch(error => alert(error))
}
}
}
</script>
Error
Whatever I try I can't figure out how to configure the desired behavior. I keep getting the following errors - > Error: Request failed with status code 404 & Cannot POST /
Note
The reason I want to do it this way is that after the user submits the form a Netlify Function will be called to send the email_adress to EmailOctopus via their API.
This is how the function looks like:
submissions-created.js
import axios from "axios";
exports.handler = async function(event) {
console.log(event.body)
const email = JSON.parse(event.body).payload.userEmail
console.log(`Recieved a submission: ${email}`)
axios({
method: 'POST',
url: `https://emailoctopus.com/api/1.5/lists/contacts`,
data: {
"api_key": apikey,
"email_address": email,
},
})
.then(response => response.json())
.then(data => {
console.log(`Submitted to EmailOctopus:\n ${data}`)
})
.catch(function (error) {
error => ({ statusCode: 422, body: String(error) })
});
}
Sorry for the long question. I really apreciate your time and your help. If you need any further details please let me know.
You can see the functional implementation in my repo (https://github.com/rasulkireev/gridsome-personal-webite). These are the changes I made.
submission-created.js
var axios = require("axios")
exports.handler = async function(event, context) {
const email = JSON.parse(event.body).payload.email
console.log(`Recieved a submission: ${email}`)
return await axios({
method: 'POST',
url: 'https://api.buttondown.email/v1/subscribers',
headers: {
Authorization: `Token ${process.env.BUTTONDOWN_API}`
},
data: {
'email': email,
},
})
.then(response => console.log(response))
.catch(error => console.log(error))
}
newsletter form in my components
<div>
<form
name="add-subscriber"
id="myForm"
method="post"
data-netlify="true"
data-netlify-honeypot="bot-field"
enctype="application/x-www-form-urlencoded"
#submit.prevent="handleFormSubmit">
<input type="hidden" name="form-name" value="add-subscriber" />
<input type="email" name="userEmail" v-model="formData.userEmail">
<button type="submit" name="button">Subscribe</button>
</form>
</div>
with the following script code in the same component
import axios from "axios";
export default {
props: ['title', 'description'],
data() {
return {
formData: {
userEmail: null,
},
}
},
methods: {
encode(data) {
const formData = new FormData();
for (const key of Object.keys(data)) {
formData.append(key, data[key]);
}
return formData;
},
handleFormSubmit(e) {
const axiosConfig = {
header: { "Content-Type": "application/x-www-form-urlencoded" }
};
axios.post(
location.href,
this.encode({
'form-name': e.target.getAttribute("name"),
...this.formData,
}),
axiosConfig
)
.then(data => console.log(data))
.catch(error => console.log(error))
.then(document.getElementById("myForm").innerHTML = `
<div>Thank you! I received your submission.</div>
`)
}
}
}

React Api Call with a File as Parameter

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

Categories

Resources