Frontend = React, backend = FastApi.
How can I simply send an image from the frontend, and have the backend saving it to the local disk ?
I've tried different ways: in an object, in a base64 string, etc.
But I can't manage to deserialize the image in FastApi.
It looks like an encoded string, I tried writing it to a file, or decoding it, but with no success.
const [selectedFile, setSelectedFile] = useState(null);
const changeHandler = (event) => {setSelectedFile(event.target.files[0]); };
const handleSubmit = event => {
const formData2 = new FormData();
formData2.append(
"file",
selectedFile,
selectedFile.name
);
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' },
body: formData2 // Also tried selectedFile
};
fetch('http://0.0.0.0:8000/task/upload_image/'+user_id, requestOptions)
.then(response => response.json())
}
return ( <div
<form onSubmit={handleSubmit}>
<fieldset>
<label htmlFor="image">upload picture</label><br/>
<input name="image" type="file" onChange={changeHandler} accept=".jpeg, .png, .jpg"/>
</fieldset>
<br/>
<Button color="primary" type="submit">Save</Button>
</form>
</div>
);
And the backend:
#router.post("/upload_image/{user_id}")
async def upload_image(user_id: int, request: Request):
body = await request.body()
# fails (TypeError)
with open('/home/backend/test.png', 'wb') as fout:
fout.writelines(body)
I also tried to simply mimic the client with something like this:
curl -F media=#/home/original.png http://0.0.0.0:8000/task/upload_image/3
but same result...
----- [Solved] Removing user_id for simplicity.
The server part must look like this:
#router.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
out_path = 'example/path/file'
async with aiofiles.open(out_path, 'wb') as out_file:
content = await file.read()
await out_file.write(content)
And for some reason, the client part should not include the content-type in the headers:
function TestIt ( ) {
const [selectedFile, setSelectedFile] = useState(null);
const [isFilePicked, setIsFilePicked] = useState(false);
const changeHandler = (event) => {
setSelectedFile(event.target.files[0]);
setIsFilePicked(true);
};
const handleSubmit = event => {
event.preventDefault();
const formData2 = new FormData();
formData2.append(
"file",
selectedFile,
selectedFile.name
);
const requestOptions = {
method: 'POST',
//headers: { 'Content-Type': 'multipart/form-data' }, // DO NOT INCLUDE HEADERS
body: formData2
};
fetch('http://0.0.0.0:8000/task/uploadfile/', requestOptions)
.then(response => response.json())
.then(function (response) {
console.log('response')
console.log(response)
});
}
return ( <div>
<form onSubmit={handleSubmit}>
<fieldset>
<input name="image" type="file" onChange={changeHandler} accept=".jpeg, .png, .jpg"/>
</fieldset>
<Button type="submit">Save</Button>
</form>
</div>
);
}
Replying from to your comment: yes there is a simple snippet example of JS-Fastapi and it was answered by me not long ago.
The reason you are not able to access the file is fairly simple: the naming of the python parameters must match the keys of the formData object.
Instead, you are accessing the raw request, which does not make sense.
Simply change your python code as follows:
from fastapi import UploadFile, File
#router.post("/upload_image/{user_id}")
async def upload_image(user_id: int, file: UploadFile = File(...)):
# Your code goes here
which is well detailed in the official docs
https://fastapi.tiangolo.com/tutorial/request-files/?h=file
FYI
The following are the references to my answers (actually checking, I answered multiple questions related to this topic and should be enough to understand the basic problems one may fall into when uploading files)
How to send a file (docx, doc, pdf or json) to fastapi and predict on it without UI (i.e., HTML)?
How can I upload multiple files using JavaScript and FastAPI?
How to upload file using fastapi with vue? I got error unprocessable 422
Uploading an excel file to FastAPI from a React app
Related
Trying to get json data to front end and have tried a bunch of versions of axios requests but keep getting 404 status code.
This is an example of the front end format:
class App extends Component {
constructor () {
super();
this.state = {
message: ''
};
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
axios.get('./hello')
.then(response => {
this.setState({ message: response.data.text });
})
.catch(error => {
console.log(error);
});
}
render () {
return (
<div className="button-container">
<button className='button' onClick={this.handleClick}>Click Me</button>
<p>{this.state.message}</p>
</div>
)
}
}
and back end routing:
#app.route('/hello')
def hello_world():
return jsonify(text='hello world')
Error message says 'Failed to load resource: the server responded with a status of 404 (Not Found)' or says http://localhost:5003/hello doesn't exist
First check your server, which url and port, your api is exposed
You need to pass complete url to axios constructor otherwise it will send request to same origin/url where your client is hosted, .e.g webapp at localhost:3000.
So your code will be
const SERVER_URL = 'http:localhost:5000'
axios.get(`${SERVER_URL}/hello`)
I'm extremely new to React and Python and just trying to do a simple post from a react form to my Python API that will interface with a mongoDB.
I have a form in react that invokes a handleSubmit function on submit. I want the handleSubmit function to POST to my Python API running on port 5000. My react app is running on port 8080.
The handleSubmit looks like this:
handleSubmit(event) {
const axios = require('axios');
const baseUrl = 'http://localhost:5000'
axios.post('http://localhost:5000/api/create', JSON.stringify(params))
.end((error, response) => {
if (!error && response) {
console.log('got a valid response from the server')
} else {
console.log(`Error fetching data from the server: `, error)
}
});
event.preventDefault();
}
Python endpoint code:
#app.route('/api/create', methods=['POST'])
def create(self):
if request.method == 'POST':
print(request.args.get('exp_title'))
return True
return False
When I click the button, my python API endpoint isn't reached because react is trying to post to a route on port 8080. What am I missing?
I've tried using a regular ajax call and get the same result. At one point, I did something and got a CORS error in the browser, but I can't remember how I did that.
To enable cors, you need to install pip install -U flask-cors,
here is the website: https://flask-cors.readthedocs.io/en/latest/
or you can define cors in proxy in your reactjs package.json like here:
https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development
Once you install cors in your python app, try this:
Python app:
#app.route('/api/', methods=['POST', 'GET'])
def api_post():
if request.method == 'POST':
print('post app')
req = request.json
print(req)
return jsonify(name='john')
React app:
function App() {
const [todos, setTodos] = useState(null);
const [value, setValue] = useState('');
function handleSubmit(e) {
e.preventDefault();
const data = { name: value };
console.log('submit');
console.log(value);
fetch('http://127.0.0.1:5000/api/', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
})
.then(res => res.json())
.then(res => console.log(res));
}
function handleValue(e) {
setValue(e.target.value);
}
return (
<section id="app">
<form action="" onSubmit={handleSubmit}>
<input type="text" onChange={handleValue} />
<button> submit </button>
</form>
</section>
);
}
render(<App />, document.querySelector('#root'));
I'm writing an hybrid web app using flask. By hybrid I mean that there is the conventional web server application built with template engine and there is RESTful API for client side application as well. So here is my confusion:
In my current application, user logs in through the web server so that an HTTP session is created, the user then can do stuff. However, in one of the pages, there is a action that is done via AJAX call to the RESTful part of the same application. Normally in this API, the user will have to authenticate itself again. But here the client side code has no way of knowing the user name and password. What's the correct pattern here?
You can authenticate the user client side in ajax call:
For example:
$.ajax({
url: 'http://example.com/api.ashx/v2/users.xml',
beforeSend: addHeaders,
dataType: "text",
processData: false,
success: function(data, status) {
// do stuff here
},
error: function(xhr, status, error) {
// do stuff here
}
});
var addHeaders = function(xhr) {
var restAuthHeader = readCookie("AuthorizationCookie");
if (restAuthHeader != null) {
xhr.setRequestHeader("Rest-Authorization-Code", restAuthHeader);
}
};
var readCookie = function(input) {
var nameEQ = input + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[ i ];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length, c.length);
}
return null;
};
Let's say you have a form with username and password to authenticate.
<form id="login-form">
<input data-key="username" type="text" placeholder="username" />
<input data-key="password" type="password" placeholder="password" />
<button type="submit">Login</button>
</form>
Your endpoint should return a token and a userid.
var $form = $('#login-form');
// post to your login endpoint with username and password
$.post('/login', {
username: $form.find('input[data-key="username"]').val(),
password: $form.find('input[data-key="password"]').val();
}).done(function (response) {
// put the token and userid in the sessionStorage or localStorage
window.sessionStorage.setItem('token', response.data.token);
window.sessionStorage.setItem('userId', response.data.userId);
}).fail(function (e) {
// handle incorrect credentials here.
alert('authentication failed');
});
You should append these to your headers to request data.
function requestEndpoint(endpoint) {
$.ajax({
// other stuff here you probably know
headers: {
'X-Auth-Token': window.sessionStorage.getItem('token'),
'X-User-Id': window.sessionStorage.getItem('userId'),
'Content-Type': 'application/json'
}
});
}
Just scan for these headers at the endpoints in flask
I am trying to upload many files at once to my CherryPy server.
I am following this tutorial that shows PHP code on the server side.
The JavaScript part is simple. Here is a summary of what it does:
function FileSelectHandler(e) {
var files = e.target.files || e.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "upload", true);
xhr.setRequestHeader("X_FILENAME", file.name);
xhr.send(file);
}
I translated the upload.php described in the tutorial into something like this:
def upload(self):
[...]
When the server receives the request I can see that cherrypy.request.headers['Content-Length'] == 5676
which is the length of the file I'm trying to upload, so I assume the whole file has been sent to the server.
How do I get the content of the file?
At its minimum it looks like the following. Tested in Firefox and Chromium. If you need to support legacy browsers I'd look at some JavaScript library for polyfills and fallback.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import shutil
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8,
}
}
class App:
#cherrypy.expose
def index(self):
return '''<!DOCTYPE html>
<html>
<head>
<title>CherryPy Async Upload</title>
</head>
<body>
<form id='upload' action=''>
<label for='fileselect'>Files to upload:</label>
<input type='file' id='fileselect' multiple='multiple' />
</form>
<script type='text/javascript'>
function upload(file)
{
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function(event)
{
console.log('progess', file.name, event.loaded, event.total);
});
xhr.addEventListener('readystatechange', function(event)
{
console.log(
'ready state',
file.name,
xhr.readyState,
xhr.readyState == 4 && xhr.status
);
});
xhr.open('POST', '/upload', true);
xhr.setRequestHeader('X-Filename', file.name);
console.log('sending', file.name, file);
xhr.send(file);
}
var select = document.getElementById('fileselect');
var form = document.getElementById('upload')
select.addEventListener('change', function(event)
{
for(var i = 0; i < event.target.files.length; i += 1)
{
upload(event.target.files[i]);
}
form.reset();
});
</script>
</body>
</html>
'''
#cherrypy.expose
def upload(self):
'''Handle non-multipart upload'''
filename = os.path.basename(cherrypy.request.headers['x-filename'])
destination = os.path.join('/home/user', filename)
with open(destination, 'wb') as f:
shutil.copyfileobj(cherrypy.request.body, f)
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
I'm using WebSockets in the Tornado Framework and can't get the data in a html form to be sent to a tornado class.
This is my code:
class MainHandler(tornado.web.RequestHandler):
event = []
def get(self):
self.render('main.html')
def post(self):
MainHandler.event = self.get_argument('event')
When I try and send event to a WebSocketHandler class. no data is received from the form:
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print "tailing..."
db = Connection().blah
coll = db.blah_tail
event = MainHandler.event
print 'Filtered', event
'Filtered' just prints an empty list: "Filtered []".
The html form:
<form action="/" method="post">
<input type="text" name="event" />
<input type="submit" id="open" value="Submit Query" />
</form>
How could you send the form data to the WSHandler class?
Thanks
The js for creating the websocket:
<script>
$(document).ready(function() {
var ws;
$("#open").click(function(evt){
evt.preventDefault();
ws = new WebSocket("ws://" + "localhost" + ":" + "8888" + "/ws");
ws.onmessage = function(evt) $("#display").append(evt.data + "<br />");
ws.onclose = function(evt) {alert("Server connection terminated");};
});
});
</script>
Just like in the example from the Tornado documentation, I'll use a set for the WebSocket clients. Improving this is left as an exercise for the reader.
# clients listing on the WebSocket
clients = set()
class MainHandler(tornado.web.RequestHandler):
def get(self):
return self.render("index.html")
def post(self):
global clients
event = self.get_argument("event")
print "got event", event
if not clients:
print "No WebSockets, no point in querying the database"
return
for coordinate in self.get_coordinates(event):
for client in clients:
print "sending coordinate", coordinate, "to client", client
client.write_message(json.dumps(coordinate,
default=json_util.default))
def get_coordinates(self, event):
# replace with a real database query
for coordinate in ("No", "man's", "land"):
time.sleep(1)
yield coordinate
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
global clients
print "WebSocket opened..."
clients.add(self)
def on_close(self):
global clients
print "WebSocket closed..."
clients.remove(self)
The relevant part of the index.html template:
<script type="text/javascript">
$(document).ready(function() {
var ws;
// open WebSocket for getting the results
ws = new WebSocket("ws://" + location.host + "/ws");
ws.onmessage = function(evt) {
$("#display").append(evt.data + "<br>");
};
ws.onclose = function(evt) {alert("Server connection terminated");};
$("#open").click(function(evt) {
evt.preventDefault();
$.post("/", $("#eventForm").serialize());
});
});
</script>
</head>
<body>
<h1>Event follower</h1>
<h2>Enter the event you would like to follow</h2>
<form id="eventForm" action="/" method="post">
<input type="text" name="event" />
<input type="submit" id="open" value="Submit Query" />
</form>
<h2>Coordinates</h2>
<div id="display">
</div>
</body>
When the page is loaded, a WebSocket connection is made to the server to the WSHandler class and the client is added to the clients set. When the page is closed, the WebSocket connection is closed and the server will remove it from the set.
When the open submit button is clicked, the form will be submitted asynchronously using AJAX to MainHandler.post. The method will find out the coordinates related to that event and send them to the listening clients as they come it. The browser receives each coordinate and it appends it to the display div.
What is the handler of your function
MainHandler or WSHandler,
Only One of them call at a single time so your syntax
event = MainHandler.event won't produce any result for you.
If your objective is only to submit the form.
Then on Submit type of event you have to write a post or get function associated with your submit call in your JS, That will work with normal tornado.web.RequestHandler on server side.
Ref. tornado web socket chat example
I have updated the chat example :
$(document).ready(function() {
if (!window.console) window.console = {};
if (!window.console.log) window.console.log = function() {};
$("#messageform").live("submit", function() {
newMessage($(this));
return false;
});
$("#message").select();
}
});
function newMessage(form) {
var message = form.formToDict();
var disabled = form.find("input[type=submit]");
disabled.disable();
$.postJSON("URL", message, function(response) {
console.log(response);
});
}
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
if (callback) callback(eval("(" + response + ")"));
}, error: function(response) {
console.log("ERROR:", response)
}});
};
When you will call $("#message").submit() you will receive form data in you "URL" function
If you want's to use WSHandler then
Ref. example link will help you.
See if this helps.