After flask redirect i have invalid data - python

I want to use Dropzone.js with Flask. After uploading file i want save file and show uploaded file name in another page (after redirect). But in browser i receive file name equals "sample_value". How to fix this?
Python
import os
from flask import Flask, render_template, request,redirect, url_for
app = Flask(__name__)
app.config['UPLOADED_PATH'] = os.getcwd() + '/upload'
#app.route('/')
def index():
# render upload page
return render_template('index.html')
#app.route('/upload', methods=['GET', 'POST'])
def upload():
n='sample_value'
if request.method == 'POST':
for f in request.files.getlist('file'):
n=f.filename
f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
print(f.filename)
# There i get real file name
n=f.filename
return redirect(url_for('found', file_name=n), code=307)
#app.route('/found',methods=['GET', 'POST'])
def found():
#There my file name is "sample_value"
print('File name after redirect ', request.args.get('file_name'))
return request.args.get('file_name')
if __name__ == '__main__':
app.run(host='0.0.0.0', port =5000, debug=True, threaded=True)
Template
<html>
<body>
<script src="{{ url_for('static', filename='js/dropzone.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.js') }}"></script>
<form action="{{ url_for('upload') }}" class="dropzone" id="my-dropzone" method="POST" enctype="multipart/form-data">
</form>
<script>
Dropzone.autoDiscover = false;
$(function() {
var myDropzone = new Dropzone("#my-dropzone");
myDropzone.on("queuecomplete", function(file) {
// Called when all files in the queue finish uploading.
window.location = "{{ url_for('upload') }}";
});
})
</script>
</body>
</html>
How i understand redirection executes before processing request?

I assume that you do not specify the method in your Dropzone, then it uses GET method by default. Check the Dropzone documentation and specify the method as POST.
n="sample_value"
if request.method == 'POST': # This is always false.
...
>>> print(n) # n has never been replaced
sample_value

Related

Python Flask: getting URL when moving to production

I'm using Python from quite some time but I am totally newbie in Flask. Can you help me with two simple questions I have been strugling two days?
I have a simple Flask app that supposed to import a XLSX or CSV, parse it and create a zip file to download. While I am begginig work on the parse part, I got an error when uploading the file, and I found out that the app is not saving the file altough it works when running flask locally.
This is the code:
test2.py
``` from flask import Flask, render_template, request
from werkzeug.utils import secure_filename
import pandas as pd
app = Flask(__name__)
def work(arquivo):
df = pd.read_excel(arquivo)
return(str(df.shape))
#app.route('/')
def start():
return "acesse /upload"
#app.route('/upload')
def upload_file():
return render_template('upload.html')
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
if __name__ == '__main__':
app.run(debug = True)
```
And this is the upload.html file that I put on templates folder in production (the app runs on http://www.fabianocastello.com.br/test2/upload):
<html>
<body>
<form action = "http://www.fabianocastello.com.br/test2/uploaded" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
when locally, the upload.html file that works is this:
<html>
<body>
<form action = "http://localhost:5000/uploader" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
The error I got after uploading the file is this:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
My questions are these:
1. Why the production app is not saving the uploaded file in "inbox" folder?
2. Is there a way that I substitute the URL in upload.html file from a variable so to I do not have to manually change the file before upload?
Thank you all in advance.
This is what the url_for method is for. That will automatically fix "http://localhost:5000/uploader" for you.
However, <form action = "http://www.fabianocastello.com.br/test2/uploaded" ...> points at a bigger misunderstanding. It would be horrendous if you had to alter every route in your templates moving from development to production. Your Flask routes needn't point to the specific domain that you're running your app on; they need only point to the endpoint of the server running your app (which might be gunicorn, for example). The Mega Tuorial might be helpful here for deployment. There's also more info in the deployment docs.
With that out of the way, there's other issues that need to be dealt with:
You have two route functions with the same name - upload_file. Why? It doesn't matter that you decorated them with different URLs, you can't have two functions with the same name in the same namespace.
The second upload_file is set to accept both GET and POST requests, but you only handle the POST case. Sending a GET request to this route will error.
This fixes the form:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
This consolidates the two route functions into one:
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
else:
return render_template('upload.html')
return 'file uploaded successfully '+a is going to give a garbage result, if any, though. It's not going to render a template with the message, it's just going to be unstyled text. It looks like you want AJAX, which would look something like this:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data" id="upload_file_form">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
<div id="response_div"></div>
</body>
<script>
$("#upload_file_form").submit(function(e) {
e.preventDefault();
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(),
context: form,
success: function(resp) {
$("#response_div").html(resp);
}
});
});
</script>
</html>

Flask Preventing Form Injection

How can python / flask block foreign form injections?
Consider the following mwe:
app.py
from flask import Flask, request, render template
app = Flask(__name__)
#app.route('/', methods=['GET','POST'])
def helloworld():
if request.method == 'GET':
return render_template('index.html')
if request.method == 'POST':
print(request.form['info'])
## do something with the info, like write to a database
return 'nothing'
if __name__ == '__main__':
app.run(debug=True)
templates/index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type='text/javascript' src="{{ url_for('static', filename='js/fire.js') }}"></script>
</head>
<body>
<p>Hello world!</p>
</body>
</html>
static/js/fire.js
$(document).click(function() {
// post data to flask
$.post('/', {'info': 'test'});
return false;
};
My questions are:
Is injection possible from a foreign website? Follow-up: how could this be done? (e.g., perhaps via a form that posts to my website url?)
If injection is possible, what can I do in the app.py script to block the injection?
Edit
Here is a very basic script that can be used to test injections against the above flask application. The accepted answer blocks this script:
<!DOCTYPE html>
<html>
<body>
<h2>Malicious Form Injection</h2>
<form action='http://127.0.0.1:5000/' method='post'>
Input 1:<br>
<input name="info" value="mal1"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
app.py
from flask import Flask, request, render template
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
CSRFProtect(app)
app.config['SECRET_KEY'] = 'somethignrandom'
#app.route('/', methods=['GET','POST'])
def helloworld():
if request.method == 'GET':
return render_template('index.html')
if request.method == 'POST': # anything post will autocheck csrf
print(request.form['info'])
## do something with the info, like write to a database
return 'nothing'
if __name__ == '__main__':
app.run(debug=True)
There is no need to pass the secret key to the html template, as CSRFProtect will automatically pass the secret key.
templates/index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<meta name='csrf-token' content="{{ csrf_token() }}">
<script type='text/javascript' src="{{ url_for('static', filename='js/fire.js') }}"></script>
</head>
<body>
<p>Hello world!</p>
</body>
</html>
script.js
$(document).click(function() {
// post data to flask
$.post('/', {'info': 'test', '_csrf_token':$('meta[name="csrf-token"]').attr('content')});
return false;
};

How to Display Image in Flask after a button is pressed

I am new to flask and web development. I want to display an image on a local web server after I press a button on the webpage. I am using Flask.Ive been trying to figure this out for a while and havent been able too so any help would be incredible.
FLASK CODE:
#app.route('/graph_select')
def graph_select():
return render_template('live_stream.html')
#app.route('/read_ph', methods=["GET"])
def ph_plot():
if request.method == "GET":
all_plots.ph_plot()
return render_template('live_stream.html')
#app.route("/read_temp", methods=["GET"])
def temp_plot():
if request.method == "GET":
all_plots.temperature_plot()
return render_template('live_stream.html')
#app.route('/read_distance', methods=["GET"])
def distance_plot():
if request.method == "GET":
all_plots.distance_plot()
return render_template('live_stream.html')
HTML CODE:
<h1>Data Monitoring Station</h1>
<form method="GET"
<button type="button">Temperature Graph</button>
<button type="button">PH Graph</button>
<button type="button">Distance Graph</button>
</form>
<h3><img src="{{ url_for('static', filename='ph_plot.png') }}" width="30%">$
<h3><img src="{{ url_for('static', filename='temperature_plot.png') }}" width="30%">$
<h3><img src="{{ url_for('static', filename='distance_plot.png') }}" width="30%">$
</body>
</html>
TLDR;
I wrote a minimal example on displaying images on button click using Flask and Ajax.
In essence, I just returned the URL of the image to the HTML page and set the src attribute of <img> tag with the returned URL.
app.py:
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def hello():
return render_template('a.html')
#app.route("/getimage")
def get_img():
return "a.jpg"
if __name__ == '__main__':
app.run()
a.html:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<button type='button' id ='retrieve'>Submit</button>
<img src="" id="myimg" />
</body>
<script>
$(document).ready(function() {
$('#retrieve').click(function(){
$.ajax({
url: "{{ url_for ('get_img') }}",
type: "GET",
success: function(response) {
$("#myimg").attr('src', '/static/' + response);
},
error: function(xhr) {
//Do Something to handle error
}
});
});
});
</script>
</html>
You can modify this code as you wish.
Note: The a.html file should be in templates folder and the a.jpg file should be in the static folder.
Hope this helps. Good luck.

Output data on same page after form submit

So I created a small flask program which would take a file , do some processing and returns a stream of data using yield.
I am using html form for file upload and submit. The form sends file to a python script and returns the output. The issue is that the output is presented onto a different page because of the form action attribute whereas I need the output on the same page. Probably inside a div tag.
index.html
<script>
if (!!window.EventSource) {
var source = new EventSource('/upload');
source.onmessage = function(e) {
console.log(e)
var byte = e.data;
var res = byte.split(/\s/);
console.log(res[0])
$("#morse").text(res[0].slice(1,));
}
}
</script>
<form action="/upload" method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div> // this is where is need my data
Python code
#app.route('/')
def index():
return render_template('index.html')
#app.route("/upload", methods=['GET', 'POST'])
def streambyte():
if request.method == 'POST':
f = request.files['file']
list_of_items = unAssign(f) # some file processing
def events():
for i in list_of_items:
yield "data: %s\n\n" % (i)
time.sleep(1) # an artificial delay
return Response(events(), content_type='text/event-stream')
This streams the data on http://localhost:5000/upload whereas I need it on http://localhost:5000.
I tried using redirect with Response but it failed saying TypeError: 'generator' object is not callable
You may not need JavaScript to do this...
Since you need the result on the 'index.html' page (i.e http://localhost:5000), you need to create two routes for the same index page.
The first route will load the fresh form (method attribute not set), while the second will reload the process form (method attribute is set to POST). Both routes will point to same index page.
Here below is how your code should look like:-
index.html
<!DOCTYPE html>
<html>
<head>
<title>Flask App - Output data on same page after form submit</title>
</head>
<body>
<form method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div>
<h3>{{ result }}</h3>
<h3>{{ file_path }}</h3>
<!-- this is where is need my data -->
</body>
</html>
Python code
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route("/", methods=['GET', 'POST'])
def streambyte():
# your file processing code is here...
f = request.files['file']
your_script_result = 'This variable holds the final data output'
# your file processing code is here...
return render_template('index.html', file_path = f, result = your_script_result)
if __name__ == '__main__':
app.run(debug=True)
Read more from this link: Send data from a textbox into Flask?

How to upload a file without changing the page

I want to upload a file in server and perform operation on that file. I am using Flask to render a template which shows a form to select a file and upload using post operation. I am also using websockets to communicate between server and client. What i want to do is:
Show index.html
User selects a file
Click on submit button
File is uploaded but url doesn't change i.e. it still shows index.html
(it redirects to /uploads)
Status is changed using websockets
Currently, what's happening is the python upload_file function requires me to return something like "File uploaded successfully" which is changing view. If i redirect to the index.html from there, a new session is created.(I may be wrong here)
Contents of index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask SocketIO Test</title>
</head>
<body>
<p>Anacall</p>
<form action="/upload" method="POST"
enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit"/>
</form>
<br>
<p id="status">Status</p>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('connect', function() {
socket.emit('get_status')
console.log('Websocket connected!');
});
socket.on('response_status',function(){
document.getElementById("status").innerHTML = "Status : File Status";
console.log("response received");
});
</script>
</body>
</html>
Contents of python file:
from flask import Flask, render_template, request, redirect, url_for
from flask_socketio import SocketIO, emit
from werkzeug import secure_filename
app = Flask(__name__)
socketio = SocketIO(app)
UPLOAD_FOLDER = '/static'
ALLOWED_EXTENSIONS = set(['xlsx', 'txt'])
status = None
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/')
def index():
return render_template('index.html')
#app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
return 'No file selected'
file = request.files['file']
if file.filename == '':
return "No file selected"
if file and allowed_file(file.filename):
file.save(secure_filename("file.xlsx")) # saves in current directory
status = "File uploaded"
return redirect(url_for('index'))
#socketio.on('get_status')
def send_status():
print("Send status now")
if (status != None):
emit("response_status")
if __name__ == '__main__':
socketio.run(app, host='10.131.65.115', port=12000)
You are right. Every time when you are reloading a page, the WebSocket-connection is closed and reopened again. To get rid of this you have to authenticate the session with session-cookies etc.
But you could just write return ('', 204) in the upload_file-Method of your python-file. This tells the client (Browser) that there is no content. The "upload"-Operation could than be realized within an iFrame.
But I would recommend you the use of DropzoneJS.
Examples with Flask:
https://github.com/greyli/flask-dropzone
https://github.com/helloflask/flask-upload-dropzone

Categories

Resources