Return multiple image files with Flask - python

First I've searched a lot and didn't find a straight forward answer to my question yet.
I'm writing a new program which accepts an image and then find similar images to it and then extract image names from a database. I've created a simple response with the name of these image files as a JSON format with Flask in python3. I need to display these images with its name under it as a web page with a response in Flask. The major problem is the number of images are not fixed and may vary, sometimes it became as 10 and sometimes as 0. Actually, I'm not familiar with Flask or HTML, but I need to do it.
Here is my sample code which response the image names as an array:
from flask import Flask, jsonify, request, redirect, send_file
import numpy
import os
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/', methods=['GET', 'POST'])
def upload_image():
# Check if a valid image file was uploaded
if request.method == 'POST':
if 'file' not in request.files:
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return redirect(request.url)
if file and allowed_file(file.filename):
# The image file seems valid! Detect faces and return the result.
return detect_faces_in_image(file)
# If no valid image file was uploaded, show the file upload form:
return '''
<!doctype html>
<title>Face System</title>
<h1>Upload a picture and see if it's a picture of database!</h1>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
'''
def detect_faces_in_image(file_stream):
... #some coding to create the founded_file_names
out=founded_file_names #array of desired file names
return jsonify(out)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5001, debug=True)
Thanks.

First things first, please don't hardcode HTML into your python file. Make a templates/ directory, and put all of this html into a file, lets say homepage.html. You can then use return render_template('homepage.html') to render the html.
Now, for the main question.
The major problem is the number of images are not fixed and may vary, sometimes it became as 10 and sometimes as 0.
That won't be a problem if you use Jinja, which is included in flask. If you're not familiar with it, have a look at the docs.
Looking at your code, the best way to do it would be the following: after the user submitted his image, you can show another page, lets say results.html(which you should put in templates/), and then show the pics there.
from flask import Flask, jsonify, request, redirect, send_file
import numpy
import os
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/', methods=['GET', 'POST'])
def upload_image():
# Check if a valid image file was uploaded
if request.method == 'POST':
if 'file' not in request.files:
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return redirect(request.url)
if file and allowed_file(file.filename):
# The image file seems valid!
# Get the filenames and pass the on to the results.html page
return render_template(
'results.html', results=detect_faces_in_image(file)
)
# If no valid image file was uploaded, show the file upload form:
return render_template('homepage.html')
def detect_faces_in_image(file_stream):
# .. do something here ...
return file_names_found # list of the filenames, no need for JSON
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5001, debug=True)
Now, the front end. Here is a rough draft of what results.html could look like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Results</title>
</head>
<body>
{% if results %}
<table>
<tbody>
{% for img_name in results %}
<tr>
<td>{{ img_name }}</td>
<td><img src="/pathtoyourimg" alt="{{ img_name }}"></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
No results :(
{% endif %}
</body>
</html>
You can see that the Jinja syntax is very similar to python (if/else, for .. in ..). To use a variable, you must surround it with {{ }}. What HTML file does is:
check whether results if an empty list
if it is, display 'no results :('
if it isn't, then display a html table. for each result, we make a row (<tr>) and display the name on the left, and the image on the right
You should store all of your images in a folder named static/ next to the python script. If you do so, src="/pathtoyourimg" becomes src="static/{{ img_name }}"

Related

Flask Cannot Display Uploaded Images

I've been following multiple Flask tutorials (but mostly this one) on creating a web app that I can use to read an image into a FastAi model. At some point, I was even able to get the images to upload into a folder, but I could never get them to display on the webpage itself no matter what I tried to do (refactor the function, move the folder to 'static', explicitly declare paths, etc.). I assume I'm probably missing something incredibly basic but I don't know enough about Flask and how web apps work to know what that is.
Edit: I keep getting 400 Bad Request pages every time I attempt to upload an image through the "index.html" page and the image doesn't appear in the folder when I inspect it with Windows Explorer.
file structure:
main.py
app
--> __init__.py
--> routes.py
main.py:
from app import app
main.py:
from flask import Flask
app = Flask(__name__)
from app import routes
routes.py:
from app import app
import os
from fastai.vision.widgets import *
from flask import Flask, render_template, request
#app.route('/', methods=['GET','POST'])
def index():
return render_template('index.html')
def get_predictions(img_path):
global learner_inf
learner_inf = load_learner('app/static/model/export.pkl')
return learn_inf.predict(img_path)
#app.route('/predict', methods=['GET','POST'])
def predict():
if request.method == 'POST':
file = request.files['file']
filename = file.filename
file_path = os.path.join('app/static/uploads', filename)
file.save(file_path)
result = get_predictions(file_path)
return render_template('predict.html', result = str(result), user_image = file_path)
index.html
<!doctype html>
<html>
<head>
<title>Grocery Classifier</title>
</head>
<body>
<h1>Grocery Classifier</h1>
<form method="POST" action="/predict" enctype="multipart/form-data">
<p><input type="file" name="file /"></p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
predict.html
<!doctype html>
<html>
<head>
<title>Grocery Classifier</title>
</head>
<body>
<h1>Grocery Classifier</h1>
<img src="{{ user_image }}" alt="Selected Image" class="img-thumbnail">
<h2>{{ result }}</h2>
</body>
</html>
When referencing static items such as images and files, use the static folder to hold them.
app = Flask(__name__, static_folder="static")
After that, include it in CSS/HTML. This is not the direct application of inserting images in your situation, but it serves as a good example.
background-image: url("bg.gif");
The url to reference the image can be literally anything, just be sure to remember it for later reference. The reference "bg.gif" seen in the CSS automatically does GET request to "yoururl.com/bg.gif" to obtain the image.
Now link that url to the site path.
#app.route('/bg.gif', methods=['GET'])
def bghome():
return send_from_directory("static", "bg.gif")
The route MUST be correct and exact and the file MUST exist. If you use a kwarg to get the image url, be sure to keep that url consistent to what you define in your flask backend pathing.

Uploading 2 individual files via Flask for Python Processing

started playing with Flask and am looking to convert a few easy python scripts to be able to run through a web interface. Mostly data tweaking tools.
So, most of the tools that I have running through the command line use 2 spreadsheets and then perform operations between them using pandas (look for differences, update information, etc).
However, I can't seem to figure out how to create the interface needed to allow for users viewing the site to have 2 upload buttons (1 for each file) and then underneath that have a process button where the magic happens.
All of the documentation I have encountered either deals with uploading a single file (and then run whatever process) or allow for multiple file uploads with a single upload button.
So, a basic template would be something like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h2>Upload Current File Here</h2>
<form method=post enctype=multipart/form-data>
<input type=file name=curfile>
<input type=submit value=Upload>
</form>
</div>
<div>
<h2>Upload New File Here</h2>
<form method=post enctype=multipart/form-data>
<input type=file name=newfile>
<input type=submit value=Upload>
</form>
</div>
</body>
</html>
Now for the app.py, this is the version that will load a single file and process.
from flask import Flask, request, render_template
import pandas as pd
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config["ALLOWED_FILE_EXTENSIONS"] = ["xlsx"]
app.config["MAX_IMAGE_FILESIZE"] = 16 * 1024 * 1024
#app.route('/', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
dfcur = pd.read_excel(request.files.get('curfile'))
return render_template('upload.html')
if __name__ == '__main__':
app.run(debug=True)
Hope what I posted makes sense, been looking through a lot of notes online and haven't found anything that addresses the workflow yet. I know the issue is that the file is uploaded and then the template is rendered, whereas I want to allow for both to be uploaded before doing anything.
After doing some research and going over the code I managed to make it work by adding an additional request.files for the additional file name.
I still have to incorporate what to do with the files, as this code is basically from the flask upload tutorial - but here is what is working now.
py file
import os
from flask import Flask, flash, request, redirect, url_for, send_from_directory
from flask.templating import render_template
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = 'uploads/'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/uploads/<name>')
def download_file(name):
return send_from_directory(app.config["UPLOAD_FOLDER"], name)
#app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# check if the post request has the file part
if 'curfile' not in request.files or 'newfile' not in request.files:
flash("Sorry, the upload didn't send all of the data!")
return redirect(request.url)
curfile = request.files["curfile"]
newfile = request.files["newfile"]
# If the user does not select a file, the browser submits an
# empty file without a filename.
if curfile.filename == "" or newfile.filename == "":
flash('You need to upload 2 files!')
return redirect(request.url)
if curfile and allowed_file(curfile.filename):
filename = secure_filename(curfile.filename)
curfile.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('download_file', name=filename))
return render_template("app2.html")
template file
<!DOCTYPE html>
<head>
<title>Test</title>
</head>
<html>
<body>
<form method="post" enctype="multipart/form-data">
<h2>Upload Current File</h2>
<input type="file" name="curfile">
<h2>Upload New File</h2>
<input type="file" name="newfile">
<p></p>
<input type="submit" value="Upload">
</form>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<div class="message_flash">{{ message }}</div>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</body>
</html>

Uploading and reading a CSV file with Flask

I'm currently in the process of making a program to upload and read csv files. I'm throwing a key error when submitting a file to be uploaded and can't really seem to figure out why and was hoping for some help. It uploaded and saved the file before I tried adding the read file functionality but after that, it started having issues. The error is saying that 'filename' is a key error even though it seemed to work fine before I tried reading the file. Help or leading me down the right path would be greatly appreciated. Thanks so much!
Views.py
from flask import render_template, request, redirect
from app import app
import os
import csv
#app.route('/', methods=["GET", "POST"])
def index():
data = []
if request.method == 'POST':
if request.files:
uploaded_file = request.files['filename'] # This line uses the same variable and worked fine
uploaded_file.save(os.path.join(app.config['FILE_UPLOADS'], uploaded_file.filename))
f = request.form['filename'] # This is the line throwing the error
with open(f) as file:
csv_file = csv.reader(file)
for row in csv_file:
data.append(row)
return redirect(request.url)
return render_template('index.html', data=data)
#app.route('/help')
def help():
return render_template('help.html')
app.config['FILE_UPLOADS'] = "C:\\Users\\Zachary\\Documents\\VSCode_Projects\\monday_webapp\\app\\static\\file\\uploads"
Index.html
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block body %}
<div class="jumbotron">
<h1 style='text-align: center'>Zach's Web Application</h1>
</div>
<div>
<p class="lead">Upload a csv file to view its data.</p>
<form method="POST" enctype="multipart/form-data" action="/">
<input type="file" id="myFile" name="filename" accept=".csv">
<input type="submit">
</form>
</div>
<div>
{{ data }}
</div>
<div>
{% endblock %}
In flask request.form["input_name"] is used to get the input data, but not for input type=files which are accesible through request.files["input_name"], always using enctype=multipart/form-data in the form. You can get more info in the oficial documentation:
https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/
On the other hand, request.files['filename'] is a FileStorage type, the function open(f) expects str, bytes or os.PathLike object, not FileStorage.
The following code should works:
from flask import render_template, request, redirect
from app import app
import os
import csv
#app.route('/', methods=["GET", "POST"])
def index():
data = []
if request.method == 'POST':
if request.files:
uploaded_file = request.files['filename'] # This line uses the same variable and worked fine
filepath = os.path.join(app.config['FILE_UPLOADS'], uploaded_file.filename)
uploaded_file.save(filepath)
with open(filepath) as file:
csv_file = csv.reader(file)
for row in csv_file:
data.append(row)
return redirect(request.url)
return render_template('index.html', data=data)
#app.route('/help')
def help():
return render_template('help.html')
app.config['FILE_UPLOADS'] = "C:\\Users\\Zachary\\Documents\\VSCode_Projects\\monday_webapp\\app\\static\\file\\uploads"

Flask, How can I response the picture on the page

I am new to Flask and web development, I want to upload a picture and process it by my deep learning application and then response the processed picture on the page, here is my frame work code
# coding: utf-8
import os
import uuid
import PIL.Image as Image
from werkzeug import secure_filename
from flask import Flask, url_for, render_template, request, url_for, redirect, send_from_directory
ALLOWED_EXTENSIONS = set(list(['png', 'jpg', 'jpeg']))
UPLOAD_FOLDER = '/tmp'
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def process_image(file_path):
"""
resize image to 32x32
:param file_path: file path
:return:
"""
img = Image.open(file_path, mode='r')
return img.resize([32,32], Image.ANTIALIAS)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
#app.route('/', methods=['GET', 'POST'])
def upload_file():
_path = None
if request.method == 'POST':
_file = request.files['file']
print(_file)
if _file and allowed_file(_file.filename):
filename = secure_filename(_file.filename)
_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
_file.save(_path)
return show_pic(deep_learning(_path))
return '''
<!DOCTYPE html>
<title>Web App/title>
<h1>Deep Learning Web App</h1>
<form action="/" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
'''
#app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
if __name__ == '__main__':
app.run()
As you can see it, I have implemented upload picture function and the function deep_learning(path), and it return the path of processed picture, I need to implement function show_pic(), how can I do that?
Create a template with your html skeleton and pass the image path to the render_template() function.
result.html
<html>
<img src="{{ image_path }}">
</html>
Add this to your view function:
return render_template('result.html', image_path=deep_learning(_path))
For this to work your files need to be located in the staticdirectory or a subdirectory.
Or you can define _file(processed file) with None value below form tag check if file not none then show it:
#app.route('/', methods=['GET', 'POST'])
def upload_file():
_path = None
_file = None
if request.method == 'POST':
_file = request.files['file']
print(_file)
if _file and allowed_file(_file.filename):
filename = secure_filename(_file.filename)
_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
_file.save(_path)
return '''
<!DOCTYPE html>
<title>Web App/title>
<h1>Deep Learning Web App</h1>
<form ...>
...
</form>
{% if _file%}
<img src="{{url_for('uploaded_file', filename=_file) }}" >
{% endif %}
'''

Flask: How to handle application/octet-stream

I want to make a multiple file-upload form.I use jQuery File Uploader. My server-side code:
#app.route("/new/photogallery",methods=["POST"])
def newPhotoGallery():
print request.files
I tried two things:
Submit form normally:
When i submit my form normally,it prints:
ImmutableMultiDict([('post_photo_gallery', FileStorage: u'' ('application/octet-stream'))])
Submit form using AJAX:
When i submit my form using AJAX,it prints:
ImmutableMultiDict([])
My first question is: Why is there a difference between AJAX request and normal request.
My second question is: How can i handle this application/octet-streamrequest in Flask/Python
My third question is: Is this a good way to use application/octet-stream ?
By the way i do not know much about application/octet-stream.Thank you very much.
Regardless of the the data encoding, you should be able to get the raw data with request.data.
In the case of application/octet-stream, you can just write request.data to a binary file.
An example handler for various data types:
from flask import json
#app.route('/messages', methods = ['POST'])
def api_message():
if request.headers['Content-Type'] == 'text/plain':
return "Text Message: " + request.data
elif request.headers['Content-Type'] == 'application/json':
return "JSON Message: " + json.dumps(request.json)
elif request.headers['Content-Type'] == 'application/octet-stream':
with open('/tmp/binary', 'wb') as f:
f.write(request.data)
f.close()
return "Binary message written!"
else:
return "415 Unsupported Media Type ;)"
The typical scenario of handling form data is already documented here.
I was unable to get a request working using application/octet-stream type posts, but have used multipart/form-data type forms in the past to upload images using flask.
I have extended what I have done in the past to support multiple upload files and this has worked leveraging werkzeug's FileStorage objects.
The key here is setting up a post based route that is looking for a request element from a form. This should allow you to POST to the route either via a standard form or an AJAX call.
Below is a simplified example that is using a form:
Template for the view:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>jQuery File Upload Example</title>
</head>
<body>
{% if err %}
<h4>{{ err }}</h4>
{% endif %}
<form action="/" method=POST enctype=multipart/form-data id="fileupload">
<input type="file" name="files" data-url="/" multiple>
<input type=submit value=Post>
</form>
{% if files %}
{% for file in files %}
<p>Uploaded: <b>{{ file }}</b> </p>
{% endfor %}
{% endif %}
</body>
</html>
Flask App
from flask import Flask, request, render_template
from werkzeug import secure_filename, FileStorage
import os
# Flask functions
app = Flask(__name__)
app.config.from_object(__name__)
DEBUG = True
# add this so that flask doesn't swallow error messages
app.config['PROPAGATE_EXCEPTIONS'] = True
#app.route('/', methods=['GET', 'POST'])
def uploader():
if request.method =='POST' and request.files.getlist('files'):
up_file_list = []
# Iterate the through a list of files from the form input field
for a_file in request.files.getlist('files'):
if a_file.filename:
# Validate that what we have been supplied with is infact a file
if not isinstance(a_file, FileStorage):
raise TypeError("storage must be a werkzeug.FileStorage")
# Sanitise the filename
a_file_name = secure_filename(a_file.filename)
# Build target
a_file_target = os.path.join('/tmp/', a_file_name)
# Save file
a_file.save(a_file_target)
up_file_list.append(a_file_name)
# Return template
if up_file_list:
return render_template('uploader.html', err=None, files=up_file_list)
else:
return render_template('uploader.html', err='No Files Uploaded', files=None)
else:
return render_template('uploader.html', err=None, files=None)
# application execution
if __name__ == '__main__':
app.run()

Categories

Resources