How to upload a file from Vue frontend to FastAPI backend - javascript

Vuejs Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="file-upload-list" class="container">
<div class="large-12 medium-12 small-12 cell">
<label>File
<input type="file" id="file" ref="file" v-on:change="handleFileUpload()"/>
</label>
<button v-on:click="submitFile()">Submit</button>
</div>
</div>
<script>
var fileList = new Vue({
data(){
return {
file: ''
}
},
el: "#file-upload-list",
methods: {
/*
Submits the file to the server
*/
submitFile(){
/*
Initialize the form data
*/
let formData = new FormData();
/*
Add the form data we need to submit
*/
formData.append('file', this.file);
console.log(this.file);
var baseURL="http://127.0.0.1:8000"
/*
Make the request to the POST /single-file URL
*/
axios.post( baseURL+'/uploadfiles',
// formData,
{
headers: {
'Content-Type': 'multipart/form-data'
},
file:this.file
}
).then(function(){
console.log('SUCCESS!!');
})
.catch(function(){
console.log('FAILURE!!');
});
console.log(formData);
},
/*
Handles a change on the file upload
*/
handleFileUpload(){
this.file = this.$refs.file.files[0];
}
}
});
</script>
</body>
</html>
FastAPI code
#app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile] = File(...)):
result=""
for file in files:
fpath = os.path.join(
STORAGE_PATH, f'{file.filename}'
)
async with aiofiles.open(fpath, 'wb') as f:
content = await file.read()
await f.write(content)
return {"message": "success"}
I am trying to upload a pdf file from vuejs to fastAPI. where pdf file will be saved in a particular location in fastAPI code mentioned. I have tested the fastapi code in postman and it works good and I also enabled cross-origin. I am new to vuejs and I don't know how to do form action functionality. Mainly I need to know HTTPFile transaction is happening in the vuejs

Related

FastAPI - Uploading multiple files using Axios raises Bad Request error

Client code:
!<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<form id="uploadForm" role="form" method="post" enctype=multipart/form-data>
<input type="file" id="file" name="file" multiple>
<input type=button value=Upload onclick="uploadFile()">
</form>
<script type="text/javascript">
function uploadFile() {
var formData = new FormData();
var imagefile = document.querySelector('#file');
formData.append("images", imagefile.files);
axios.post('http://127.0.0.1:8000/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
</script>
</body>
</html>
Server code:
from fastapi import FastAPI, File, UploadFile, FastAPI
from typing import Optional, List
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
...
def save_file(filename, data):
with open(filename, 'wb') as f:
f.write(data)
print('file saved')
#app.post("/upload")
async def upload(files: List[UploadFile] = File(...)):
print(files)
for file in files:
contents = await file.read()
save_file(file.filename, contents)
print('file received')
return {"Uploaded Filenames": [file.filename for file in files]}
I get the following error:
←[32mINFO←[0m: 127.0.0.1:10406 - "←[1mPOST /upload HTTP/1.1←[0m" ←[31m400 Bad Request←[0m
I have tried to upload a single file via form action and all works fine, but I need to upload two files.
First, when uploading files or form data, one should use the same form key defined in their endpoint/route. In your case, that is files. Hence, on client side you should use that key instead of images.
Second, the way to upload multiple files is to loop through the array of files (i.e., imagefile.files in your code) and add each file to the FormData object.
Third, the error seems to occur due to some bug/changes in the 0.27.1 version of Axios. Here is a recent related issue on GitHub. Using the latest version, i.e., 0.27.2, from cdnjs here resolves the issue.
Alternatively, you could use Fetch API, similar to this answer (you can add the list of files in the same way as shown in the Axios example below). In Fetch API, when uploading files to the server, you should not explicitly set the Content-Type header on the request, as explained here.
Working Example (using Axios)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Upload Files</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>
</head>
<body>
<input type="file" id="fileInput" multiple><br>
<input type="button" value="Upload" onclick="uploadFile()">
<script type="text/javascript">
function uploadFile() {
var fileInput = document.querySelector('#fileInput');
if (fileInput.files[0]) {
var formData = new FormData();
for (const file of fileInput.files)
formData.append('files', file);
axios({
method: 'post',
url: '/upload',
data: formData,
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error);
});
}
}
</script>
</body>
</html>
Starting from Axios v 0.27.2 you can do this easily:
axios
.postForm("https://httpbin.org/post", document.querySelector("#fileInput").files)
All the files will be submitted with files[] key.
More verbose example:
axios.postForm("https://httpbin.org/post", {
"myField": "foo"
"myJson{}": {x:1, y: 'bar'},
"files[]": document.querySelector("#fileInput").files
})

How to use JSON response from FastAPI backend to create a chart using Chart.js in the frontend? [duplicate]

This question already has answers here:
How do I capture response of form.submit
(20 answers)
Closed 12 months ago.
I have an HTML page being rendered with FastAPI:
#app.get("/index", response_class=HTMLResponse)
def write_home(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
Rendered HTML
The above HTML has a Submit button for a Form, which will result in a POST request the endpoint \SWP
<form id="generate" action="swp" method="post" enctype="multipart/form-data">
It works like I'd need it to - it posts the data to my Python calculator app, which sends a JSON response back:
Output from Py App
With a JSON file saved locally on the hard drive, I was able to code JavaScript which could read its data and create a Chart via Chart.js.
I used Fetch API and then used the object array:
const API_URL = "./Example_Response.json";
async function createChartAndTable() {
let ResponseFromAPI = await fetch(API_URL);
let JSON_Res = await ResponseFromAPI.json();
let JSON_Data1 = JSON.parse(JSON_Res.output.data);
From locally saved JSON file
Now, Im completely puzzled as to how Im to use the Live JSON result from the Python app http://127.0.0.1:8000/swp (Step2) and then render it to the HTML generated in Step 1 to http://127.0.0.1:8000/index like I was able to with the locally stored JSON file as in Step 3.
Note: there's no database involved.
Any guidance and help is much appreciated.
Option 1
Post the request through HTML form and then have the API redirect you to a new page (Jinja2Template), where you can read the data and display the chart. As #tiangolo posted here, when performing a RedirectResponse from a POST request route to GET request route, the response status code has to change to status_code=status.HTTP_303_SEE_OTHER (the example below does that).
Update: Redirection is not really necessary here. You can just render the template as usual:
#app.post("/submitUsingForm", response_class=HTMLResponse)
def submitUsingForm(request: Request, input1: str = Form(...), input2: str = Form(...)):
return templates.TemplateResponse("chart.html", {"request": request, "json_data": json_data})
Option 2
Use Fetch API to post the request, receive the JSON data and display the chart on the same page.
Both options are demonstrated in the example given below (the data used is sample data for the purposes of this demo; you can change/handle them as required).
app.py
from fastapi import FastAPI, Request, status, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.responses import RedirectResponse
app = FastAPI()
templates = Jinja2Templates(directory="templates")
json_data = {"jsonarray": [{"name": "Joe", "age": 32}, {"name": "Tom", "age": 34}]}
#app.get("/chart", response_class=HTMLResponse)
def get_chart(request: Request):
return templates.TemplateResponse("chart.html", {"request": request, "json_data": json_data})
#app.post("/submitUsingFetch")
def submitUsingFetch(request: Request, input1: str = Form(...), input2: str = Form(...)):
return json_data
#app.post("/submitUsingForm", response_class=HTMLResponse)
def submitUsingForm(request: Request, input1: str = Form(...), input2: str = Form(...)):
#redirect_url = request.url_for('get_chart')
#return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
return templates.TemplateResponse("chart.html", {"request": request, "json_data": json_data})
#app.get("/", response_class=HTMLResponse)
def home(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
templates/index.html
<!DOCTYPE html>
<html>
<head>
<title> Home</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script>
function send_request() {
const formElems = document.getElementById('my_form');
var formData = new FormData(formElems);
const url = "http://127.0.0.1:8000/submitUsingFetch";
fetch(url, {
method: 'POST',
headers: {'Accept': 'application/json'},
body: formData
}).then(resp => {
return resp.json();
}).then(body => {
display_chart(body)
}).catch(error => {
console.error(error);
});
}
function display_chart(json_data) {
var labels = json_data.jsonarray.map(function(e) {return e.name;});
var data = json_data.jsonarray.map(function(e) {return e.age;});
const ctx = document.getElementById('myChart');
var config = {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Graph Line',
data: data,
backgroundColor: 'rgba(0, 119, 204, 0.3)'
}]
}
};
var chart = new Chart(ctx, config);
}
</script>
</head>
<body>
<form action="/submitUsingForm" method='post' id='my_form'>
<label>Input 1:</label><br>
<input type="text" id="input1" name="input1" value="0"><br>
<label>Input 2:</label><br>
<input type="text" id="input2" name="input2" value="0"><br>
<input type="submit" value="Submit using Form">
<input type="button" value="Submit using Fetch" onclick="send_request()">
</form>
<canvas id="myChart"></canvas>
</body>
</html>
templates/chart.html
<html>
<head>
<head>
<title> Chart</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script>
function display_chart(){
var json_data = {{json_data|tojson}};
var labels = json_data.jsonarray.map(function(e) {return e.name;});
var data = json_data.jsonarray.map(function(e) {return e.age;});
const ctx = document.getElementById('myChart');
var config = {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Graph Line',
data: data,
backgroundColor: 'rgba(0, 119, 204, 0.3)'
}]
}
};
var chart = new Chart(ctx, config);
}
</script>
</head>
<body onload="display_chart()">
<canvas id="myChart"></canvas>
</body>
</html>
Got it to work with the below code. The trick like #The Fool suggested in one of his/her comments
document.forms['myFormId'].addEventListener('submit', (event) => {
event.preventDefault();
// TODO do something here to show user that form is being submitted
fetch(event.target.action, {
method: 'POST',
body: new URLSearchParams(new FormData(event.target)) // event.target is the form
}).then((resp) => {
return resp.json(); // or resp.text() or whatever the server sends
}).then((body) => {
// TODO handle body
}).catch((error) => {
// TODO handle error
});
});
What I did was place my already written JS code within "Todo handle body".
That worked!

fetch response 404 but page exists

I'm have this Fetch code (POST) but the response says status: 404 even though when I open the url in the browser, the page exists and returns a JSON. when I changed the url to https://httpbin.org/post it returns a normal data.. and when I use the same url but with GET method (without any init parameters for the fetch method) it returns status: 200.
what am I doing wrong?
when I open the url in the browser:
php controller
/*
* filepath: application/modules/test/controllers/test.php
*/
public function homepage()
{
$this->load->view('home', $this->data);
}
public function get_result()
{
$response = [
'status' => 0,
'message' => 'abcde',
];
echo json_encode($response);
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="button" id="btn1" value="Button1">
<script src="/assets/js/test_fetch.js"></script>
</body>
</html>
javascript
/*
* filepath: /assets/js/test_fetch.js
*/
/*!
* Filename: test_fetch.js
* Tanggal: 20220214
* Author: david santana
* script utk belajar ttg penggunaan fetch API
* Copyright Gotravelly.com#2022
*/
const myBtn1 = document.querySelector('#btnSubmit');
console.log(myBtn1);
myBtn1.addEventListener('click', function() {
// fetch data from server
const url = '/test/get_results';
// const url = 'https://httpbin.org/post';
let myData = {
user_id: 123,
name: 'david',
};
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(myData)
})
.then(response => console.log(response));
});
never mind, my colleague pointed out to me that I didn't add the csrf and I need to use FormData in the body parameter.. after I tried it, it works!
but then I tried to use Object with the same csrf key-value pair like this, but it doesn't work
let myData = {
user_id: 123,
name: 'david',
};
let csrf_name = document.querySelector('#csrf').attributes['name'].value;
let csrf = document.querySelector('#csrf').value;
myData[csrf_name] = csrf;
which brings me to a new question (if I may ask a follow-up question): does POST needs to always use a FormData as body parameter?

request json from html using JavaScript

I am unable to request JSON from HTML using request.
I am using the request package from npm.
<!DOCTYPE html>
<html lang="en">
<head>
<title>MySite!</title>
</head>
<body>
<h2>
its ya boi
</h2>
<button onclick=ree>workkkkk</button>
<script>
const request = require('request')
function ree() {
request('https://api-quiz.hype.space/shows/now',(error,response,body) => { //requests data from the hq trivia api
//bodys = JSON.parse(body)
//document.getElementById("para").innerHTML = (bodys.nextShowPrize)
//console.log(`Next game: ${body.nextShowTime}`)
alert(body)
})}
</script>
</body>
</html>
You could use fetch.
fetch('https://api-quiz.hype.space/shows/now')
.then(res => res.json())
.then(console.log)

Returning HTML with Azure Serverless Function req.body

I've got some TIF files in Azure Blob Storage. I'd like to display them in the browser via a link embedded in a spreadsheet. The simplest way to do this should be to take the file code as a request parameter and return the properly formatted HTML, right?
So right now I've got it returning a req.body with some HTML. Unfortunately, the HTML just shows up as a string in the browser. How do I make it render as HTML with minimal rigamarole?
Here's my code:
if (req.query.blob) {
let blob = req.query.blob;
context.res = {
// status: 200, /* Defaults to 200 */
body: `<object width=200 height=200
data="<baseaddress>/${blob}.tif" type="image/tiff">
<param name="src" value="<baseaddress>/${blob}.tif">
<param name="negative" value="yes">
</object>`
};
}
You need to set the headers to specify the content type to HTML and the response must be a full valid HTML page (with the <html> tag and the rest).
Example:
module.exports.hello = (event, context, callback) => {
const html = `
<!doctype html>
<html>
<head>
<title>The Page Title</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>`;
const response = {
statusCode: 200,
headers: {
'Content-Type': 'text/html'
},
body: html
};
callback(null, response);
};

Categories

Resources