I have an MWE of the problem in the following html template:
<block body>
<h2> Test Report </h2>
<form method="post">
<input type="submit" value="Generate Report">
</form>
</block body>
With the associated flask blueprint:
#bp.route('/reports2', methods=('GET', 'POST'))
#login_required
def reports2():
if request.method == 'POST':
from io import BytesIO
byte_obj = BytesIO()
byte_obj.write(b'Hello, I am a test file')
return send_file(byte_obj, as_attachment=True, download_name='test-downloaded-report.txt', mimetype='text/plain')
return render_template('report/reports2.html')
The result is bewildering. There's no file created in my downloads directory called 'test-downloaded-report.txt'. Instead, it downloads reports2.html!
I also originally just had send_file without being returned, instead returning a redirect to reports2.html so that the form would reset. Nothing happened there, so I assume the return is necessary for anything to happen, but as an aside I also don't see how I should send a file for download and redirect to a different page.
Any idea why that file is getting sent, instead of the file I created?
The file is not served as expected because the pointer is at the end of the stream. By putting the pointer at the beginning, the data can be read completely and should download as expected.
#bp.route('/reports2', methods=('GET', 'POST'))
#login_required
def reports2():
if request.method == 'POST':
from io import BytesIO
byte_obj = BytesIO()
byte_obj.write(b'Hello, I am a test file')
byte_obj.seek(0)
return send_file(
byte_obj,
as_attachment=True,
download_name='test-downloaded-report.txt',
mimetype='text/plain'
)
return render_template('report/reports2.html')
Forwarding after downloading or resetting the form is a little more complicated. As you correctly noticed, send_file is a response from the server. It cannot be used in conjunction with a redirect.
However, the following suggestion shows you how to use JavaScript to send the form, download the file and then reset the form. With a little adjustment you should also be able to forward to another location.
from flask import (
Flask,
render_template,
request,
send_file
)
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
from io import BytesIO
byte_obj = BytesIO()
byte_obj.write(b'Hello, I am a test file')
byte_obj.seek(0)
return send_file(
byte_obj,
as_attachment=True,
download_name='test-downloaded-report.txt',
mimetype='text/plain'
)
return render_template('index.html')
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Generate Report</title>
</head>
<body>
<h2>Test Report</h2>
<form name="my-form" action="{{ url_for('index') }}" method="post">
<input type="submit" value="Generate Report">
</form>
<script type="text/javascript">
(function() {
const download = (url, form, callback) => {
fetch(url, {
method: 'post',
body: new FormData(form)
}).then(resp => {
resp.ok && resp.blob().then(blob => {
callback(blob, resp.headers);
});
});
};
const save = (data, filename) => {
const elem = document.createElement('a');
elem.href = URL.createObjectURL(data);
elem.download = filename;
elem.click();
};
const form = document.querySelector('form[name="my-form"]');
form.addEventListener('submit', evt => {
evt.preventDefault();
download(evt.target.action, evt.target, (data, headers) => {
// Extract the filename from the "Content-Disposition" header.
const disposition = headers.get('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
const filename = matches[1].replace(/['"]/g, '');
save(data, filename);
}
}
});
// Reset the form or redirect to another location.
evt.target.reset();
})
})();
</script>
</body>
</html>
Related
I am trying to build a Flask application to upload files to a GS bucket. I ran app.py on localhost, and when I try to submit the files, the server raises the 405 Method Not Allowed error. I searched everywhere, and nothing seems to help. Here is my code:
HTML Form:
<form action="/" method="POST" enctype="multipart/form-data">
<input
class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
id="multiple_files" type="file" multiple>
app.js:
document.getElementById("request").addEventListener("click", function (event) {
event.preventDefault();
const files = document.getElementById("multiple_files").files;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append("multiple_files", files[i]);
}
fetch("/", {
method: "POST",
headers: {
'Access-Control-Allow-Origin': '*'
},
body: formData
})
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
});
app.py
import os
import uuid
from flask import Flask, request, redirect, render_template
from google.cloud import storage
app = Flask(__name__)
# Set the bucket name and json authentication file values
bucket_name = "BUCKET_NAME"
auth_file = "AUTH_FILE"
# Initialize Google Cloud Storage client
client = storage.Client.from_service_account_json(auth_file)
bucket = client.get_bucket(bucket_name)
# Route for the file upload form
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
# Get the uploaded files
files = request.files.getlist("multiple_files")
# Upload each file to the Google Cloud Storage bucket
for file in files:
# Generate a unique file name
filename = str(uuid.uuid4()) + "-" + file.filename
blob = bucket.blob(filename)
blob.upload_from_file(file)
return redirect("/")
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Can you please help me? I just started learning web development, and I cannot find any resource to help me fix this. Thanks!
I tried to change the "POST" to "PUT" but it also did not help.
I'm working on an application that requires me to upload csv files to a FLASK server from an Angular frontend. i am having difficulties doing that. how can i connect the angular frontend to the backend flask server.
Here's my component.html
<div [hidden] = "submitted">
<form (ngSubmite) = "onSubmit()" action = "http://localhost:5000" method = "post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type = "submit" value="Upload">
</form>
Here's my component.ts
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-upload',
templateUrl: './upload.component.html',
styleUrls: ['./upload.component.css']
})
export class UploadComponent implements OnInit {
submitted = false;
constructor() { }
onSubmit(){
this.submitted = true;
}
ngOnInit() {
}
}
here's my flask server
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
#app.route('/upload' , method = ['GET' ,'POST'])
def upload_File():
if request.method == 'POST':
#check if the psot request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
#if the user does not select file , browser alsos
#submite an empty part without filename
if file.filename == ' ':
flash('No selected file')
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
#return render_template('success.html', name = filename)
return redirect(request.url)
return 'File Uploaded'
Import HttpClientModule in your AppModule's imports.
Inject HttpClient in your component (move that logic into a service later) like this constructor(private readonly http: HttpClient) {}
And finally in your submit method you can do an http request this.http.post(url, body).subscribe()
I am using flask (python) to host a interface that allows the user to upload an image, and use a slider to change the value of a variable in a function that reduces noise in an image.
The slider works but the problem is I have to reload the page every time that I want to see the change in value on the updated image.
how can I add a slider that will update the image in real time so I won't have to continuously reload the page to see the changes?
(If the slider is at 0 I want it to to look how the image did when it first uploaded)
I'm doing some searching and it looks like I would use jquery or something but I don't know how to implement it
thanks for reading
app.py:
import os
from flask import Flask, render_template, request, send_from_directory,url_for, session, redirect
import cv2
import shutil
app = Flask(__name__)
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
app.config['UPLOAD_FOLDER'] = os.path.join(APP_ROOT, 'images')
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
#app.route("/")
def index():
session.clear()
return render_template("upload.html")
#app.route('/images/<filename>')
def uploadfile(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],filename)
#app.route('/home')
def home():
return render_template("completed.html", imgSrc="images/" + session.get('imgSrc') , message=session.get('message'))
#app.route("/upload" , methods = ['POST'])
def upload():
target = os.path.join(APP_ROOT, 'images')
if request.method == 'POST':
if not os.path.isdir(target):
os.mkdir(target)
for file in request.files.getlist("file"):
filename = file.filename
destination = "/".join((target, filename))
file.save(destination)
filename = destination
org = open(filename, 'rb')
base = os.path.basename(filename)
dir = os.path.dirname(filename)
filename_cp = os.path.splitext(base)[0]
filename_cp = "cp_"+filename_cp+os.path.splitext(base)[1]
destination2 = dir+"/"+filename_cp
file.save(destination2)
cpy = open (destination2, 'wb')
shutil.copyfileobj(org, cpy)
session['image'] = filename
session['filename'] = filename
session['imgSrc'] = os.path.basename(destination)
session['cimgsrc'] = os.path.basename(destination2)
session['cpimg'] = destination2
print("session", session)
return render_template("completed.html",imgSrc="images/"+session.get('imgSrc'))
#app.route("/imgp/nr", methods=['post'])
def nr():
print(session)
img = cv2.imread(session.get('cpimg'), 0)
#threshold = 40
threshold = float(request.form['slider'])
cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY, img)
print (session['cpimg'])
cv2.imwrite(session.get('cpimg'),img)
session['message'] = "NR is done!"
session['imgSrc'] = os.path.basename(session['cpimg'])
return redirect(url_for('home', op='nr'))
if __name__ =="__main__":
app.secret_key = "abcdefghijklmnopqrstuvwxyz"
app.run(port = 4555, debug = True)
upload.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload</title>
</head>
<form id ="upload-form" action="{{ url_for ('upload') }}" method = "POST" enctype="multipart/form-data">
<input type="file" name="file" accept = image/* multiple>
<p>Drag your files here or click in this area.</p>
<button type ="submit" value="Upload"> Upload</button>
</form>
<body>
</body>
</html>
completed.hmtl (where the slider and uploaded image is):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> file uploaded</h1>
<img src="{{imgSrc}}" style="height:200px;width:300px;"/>
<h2>{{message}}</h2>
<form action ="imgp/nr" method = "post">
<input type="range" min={{min}} max={{max}} value={{max}} class="slider" id="myRange" name="slider">
<input type="submit" value="Apply ga">
</form>
</body>
</html>
The short answer is you will need to do this with Javascript/jQuery. There's a similar question here covers two ways to do it:
Either load the (base64 converted) image via ajax; or
Change the src of the image on the page using jquery, to have it reload.
You will likely need to change a few parts of your code to get this all working, but it may be something along the lines of:
app.py:
Change your nr() function to GET, and return a base64 encoded image, see here for example (upload_file() function)
completed.html:
Add an ajax (the following is jquery) call to /imgp/nr that changes the display of the image on the page, when the slider is changed. For example:
...
<img id="yourImage" style="height:200px;width:300px;">
...
<script type="text/javascript">
$("#myRange").change(function(){
var sliderVal = $(this).val();
$.ajax({
medthod: 'POST',
url:'/imgp/nr/',
data: JSON.stringify({slider: sliderVal}),
success: function(data){
$("#yourImage").attr('src', 'data:image/png;base64, ' + data);
}
});
});
</script>
This code may need some fixing, I don't have much time to make it perfect sorry. Hopefully you get the idea!
For example, if I have the following code in index.html:
<div id='1'></div>
<div id='2'></div>
<div id='3'></div>
And, I have the following code in Python:
from flask import *
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
So, among the three div blocks. When I click on any one of them. I want the program to know which one I click on, and pass the value of id (1,2,3) into the data variable in python so that I can use it on "expert.html".
What are some good ways I can achieve it? Thank you in advanced!
Instead of divs, you can use buttons. That way, ajax can be utilized in the front end to retrieve the id of the button clicked and pass it to the backend:
"index.html":
<html>
<body>
<button id='1'>Button1</button>
<button id='2'>Button2</button>
<button id='3'>Button3</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('button').click(function(event) {
var the_id = event.target.id;
$.ajax({
url: "/get_id",
type: "get",
data: {the_id: the_id},
success: function(response) {
window.location.replace('/experts');
},
error: function(xhr) {
//Do Something to handle error
}
});
});
</script>
</html>
Then, a route to receive the id can be created, the results from the ajax can be stored in flask.session, and a "success" object can be passed back to the ajax in the index.html template. From the jquery in the template, the app can be redirected to /expert:
import flask
app = flask.Flask(__name__)
app.secret_key = 'SOME_SECRET_KEY'
#app.route('/get_id')
def expert():
button_id = flask.request.args.get('the_id')
flask.session['button_id'] = button_id
return flask.jsonify({'success':True})
#app.route('/experts', methods=['GET'])
def experts():
return render_template("experts.html", data=flask.session['button_id'])
I'm trying to send an image file in a FormData using an Ajax POST request.
I am faced with 2 problems:
I do not know how to extract the FormData on the flask part
I 500 internal server error when making an ajax POST request (not sure if this is because of 1)
Thank you
Flask python code:
#app.route('/', methods=['GET','POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file: # and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(os.getcwd()+"/static", "current_image.jpg"))
return jsonify({'tasks': tasks})
HTML and Javascript code:
<input id="pictureInput" type=file name=file>
<input type=submit value=Upload id="button">
<script type="text/javascript">
var pictureInput = document.getElementById("pictureInput");
var myFormData = new FormData();
myFormData.append('pictureFile', pictureInput.files[0]);
$("#button").click(function(){
console.log(pictureInput);
console.log(pictureInput.files[0]);
console.log(myFormData);
$.ajax({
url: "http://localhost:8000/",
type: 'POST',
processData: false, // important
contentType: false, // important
dataType : 'json',
data: myFormData,
success : function(data){
console.log(data);
},
});
});
</script>
Error:
The following code should work for you. You need to have the static folder in the same level as your app.py file
app.py
import os
from flask import Flask, request, jsonify
from werkzeug.utils import secure_filename
app = Flask(__name__)
#app.route('/', methods=['GET','POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file:
filename = secure_filename(file.filename)
file.save(os.path.join(os.getcwd()+"/static", "current_image.jpg"))
tasks = []
return jsonify({'tasks': tasks})
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)
tasks is not defined above in your code, so I just initialized it to an empty list. You need also to make sure that jQuery is loaded in your template.
1. I do not know how to extract the FormData on the flask part
In order to extract the fomrdata, you could write the following code
#app.route('/', methods=['GET','POST'])
def upload_file():
if request.method == 'POST':
file = request.files['pictureFile'] # according to the name you append to formdata
2. I 500 internal server error when making an ajax POST request (not sure if this is because of 1)
Actaully, if the file is not be found, there is no correct response, so it will not work correctly.
You could reference the following sample code
#app.route('/', methods=['GET','POST'])
def upload_file():
if request.method == 'POST':
isSuccess = False
if 'file' not in request.files:
return jsonify({"IsSuccess" : isSuccess, "Message": "No file part"})
file = request.files['pictureFile'] # according to the name you append to formdata
if file: # and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(os.getcwd(), "static", "current_image.jpg"))
isSuccess = True
tasks = []
return jsonify({"IsSuccess" : isSuccess, "tasks": tasks})
return jsonify({"IsSuccess" : isSuccess, "Message": "Error occurs"})