I am toying around with an image uploader using Flask. I cloned it from this repository https://github.com/chokepoint/flaskgur . I have it running on my VPS here
http://107.170.119.38
What I'm trying to do is enable someone to write a custom label for an image they are about to upload. The final product should display an image and the custom label the user typed in. The image uploader works fine but I can't seem to store the label in the database on upload. Here is my code
from flask import Flask, request, g, redirect, url_for, abort, render_template,send_from_directory
from werkzeug import secure_filename
from hashlib import md5
from PIL import Image
import sqlite3
import os
import time
DEBUG = True
BASE_DIR = '/var/www/flaskgur/'
UPLOAD_DIR = BASE_DIR + 'pics'
DATABASE = BASE_DIR + 'flaskgur.db'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config.from_object(__name__)
# Make sure extension is in the ALLOWD_EXTENSIONS set
def check_extension(extension):
return extension in ALLOWED_EXTENSIONS
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
# Return a list of the last 25 uploaded images
def get_last_pics():
cur = g.db.execute('select * from pics order by id desc limit 25')
filenames = [dict(id=row[0], filename=row[1], label=row[2]) for row in cur.fetchall()]
#filenames = [row[0] for row in cur.fetchall()]
return filenames
# Insert filename into database
def add_pic(filename, label):
g.db.executemany('insert into pics (filename, label) values (?, ?)', [filename, label])
g.db.commit()
# Generate thumbnail image
def gen_thumbnail(filename):
height = width = 200
original = Image.open(os.path.join(app.config['UPLOAD_DIR'], filename))
thumbnail = original.resize((width, height), Image.ANTIALIAS)
thumbnail.save(os.path.join(app.config['UPLOAD_DIR'], 'thumb_'+filename))
def add_label(label):
label = request.form['label']
g.db.execute('INSERT INTO pics (label) VALUES (?)', [label])
g.db.commit()
# Taken from flask example app
#app.before_request
def before_request():
g.db = connect_db()
# Taken from flask example app
#app.teardown_request
def teardown_request(exception):
db = getattr(g, 'db', None)
if db is not None:
db.close()
#app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
#app.route('/', methods=['GET','POST'])
def upload_pic():
if request.method == 'POST':
file = request.files['file']
try:
extension = file.filename.rsplit('.', 1)[1].lower()
except IndexError, e:
abort(404)
if file and check_extension(extension):
# Salt and hash the file contents
filename = md5(file.read() + str(round(time.time() * 1000))).hexdigest() + '.' + extension
file.seek(0) # Move cursor back to beginning so we can write to disk
file.save(os.path.join(app.config['UPLOAD_DIR'], filename, label))
add_pic(filename)
add_label(label)
gen_thumbnail(filename)
return redirect(url_for('show_pic', filename=filename))
else: # Bad file extension
abort(404)
else:
return render_template('upload.html', pics=get_last_pics())
#app.route('/show')
def show_pic():
filename = request.args.get('filename','')
return render_template('upload.html', filename=filename, label=label)
def show_label():
g.db = connect_db()
cur = g.db.execute('SELECT label FROM pics WHERE id=(?)')
labels = cur.fetchone()
return render_template('upload.html', labels=labels)
#app.route('/pics/<filename>')
def return_pic(filename):
return send_from_directory(app.config['UPLOAD_DIR'], secure_filename(filename))
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0')
And my template (upload.html):
{% if filename is defined %}
<div id="image"><img class="new" src="{{ pic_path(filename) }}"></div>
<h2><p>The Label is: </p>{{ labels }}</h2>
{% else %}
<ul>
{% for pic in pics %}
<li class="thumb"><img class="thumb" src="{{ pic_path('thumb_'+pic) }}"></li>
{% endfor %}
base template (base.html):
<div id="upload">
<h1>Upload Picture</h1>
<form action="/" method=post enctype=multipart/form-data>
<p>
<input type=file name=file><br />
<p>Add a Label:</p>
<input type=text name=label value={{ request.form.label }}><br />
<input type=submit value=Upload>
</p>
</div>
I apologize if my code is atrocious. I am trying my best to learn
I figured it out. Rewrote the get_last_pics() function to this:
# Return a list of the last 25 uploaded images
def get_last_pics():
try:
cur = g.db.execute('select filename, label from pics order by id desc limit 25')
filenames = []
for row in cur.fetchall():
filenames.append({"filename": row[0], "label": row[1] or ''})
return filenames
except:
return []
rewrote add_pic() to this:
# Insert filename and label into database
def add_pic(filename, label):
g.db.execute('insert into pics (filename, label) values (?, ?)', [filename, label])
g.db.commit()
rewrote upload_pic() to this:
#app.route('/', methods=['GET', 'POST'])
def upload_pic():
if request.method == 'POST':
file = request.files['file']
label = request.form['label']
try:
extension = file.filename.rsplit('.', 1)[1].lower()
except IndexError, e:
abort(404)
if file and check_extension(extension):
# Salt and hash the file contents
filename = md5(file.read() + str(round(time.time() * 1000))).hexdigest() + '.' + extension
file.seek(0) # Move cursor back to beginning so we can write to disk
file.save(os.path.join(app.config['UPLOAD_DIR'], filename))
add_pic(filename, label)
gen_thumbnail(filename)
return redirect(url_for('show_pic', filename=filename))
else:
# Bad file extension
abort(404)
else:
return render_template('upload.html', pics=get_last_pics())
rewrote show_pic() to this:
#app.route('/show')
def show_pic():
filename = request.args.get('filename', '')
t = (filename,)
cur = g.db.execute('select label from pics where filename=?', t)
label = cur.fetchone()[0]
return render_template('upload.html', filename=filename, label=label)
Everything works now. Hope that helps someone in the future
Related
i want to display both an image and its information simultaneously on an html page.
Please see my code below. i want that the value of 'temp' will change each time global_temp is updated from function render(video).
(Please pay attention in the #comment of python, includes: #1 and #2)
index.html page:
{% extends 'base.html' %}{% block content %}
<h1 style="text-align: center;margin-top:15px;">{{temp}}</h1>
<img src="{{ url_for('video') }}"/>
{% endblock %}
main.py:
from flask import Flask, render_template, Response
from video import Video
from file import LoadFile
import cv2
import ssl
import datetime
import os
app = Flask(__name__)
global_temp = 1
#app.route('/')
def index():
global global_temp
return render_template('index.html',
temp=global_temp) #1. update temp for html (from render video)
def render(video):
print('file: ')
t = 0
file = LoadFile()
loadFileName = ''
while True:
t+=1
loadFile = file.get_files()
if(loadFile['success']):
try:
cap = cv2.VideoCapture(loadFile['file'])
length = int(cv2.VideoCapture.get(cap, int(cv2.CAP_PROP_FRAME_COUNT)))
j = 0
if(loadFileName != loadFile['file']):
loadFileName = loadFile['file']
while True and (length > 0 and j < length):
j+=1
try:
global global_temp
global_temp = j #2. update for html
frame = video.get_frame(cap)
yield(b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
except Exception as e:
print('Error frame: ', str(e))
break
except Exception as e:
print('Main: ',str(e))
#app.route('/video')
def video():
return Response(render(Video()),
mimetype='multipart/x-mixed-replace; boundary=frame')
app.run(debug=True)
I'm trying to get a .csv file from a form, get it on my server, open it and treat the nunmers to return the valid ones, but when I try to open the file from the request I get "werkzeug.exceptions.BadRequestKeyError"
My html is this:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Flask Tutorial</title>
</head>
<body>
<form action="/api/listtels" method="get">
<label for="tels">file:</label>
<input name="tels" type="file" id="tels"><br><br>
<input type="submit" value="send">
</form>
</body>
</html>
And my python code is this:
import flask
import csv
from flask import Flask, render_template, request, jsonify
app = flask.Flask(__name__)
app.config["DEBUG"] = True
#app.route('/', methods=['GET'])
def home():
return render_template("home.html")
#app.route('/api/listtels', methods=['GET'])
def ltels():
tels = "ERROR"
ret = "ERROR"
if 'tels' in request.args:
tels = request.files['tels']
ret = "tel\n"
else:
return("\'tels\' arg is needed")
with open(tels, encoding='UTF-8') as f:
rows = csv.reader(f,delimiter=",",lineterminator="\n")
next(rows, None)
for row in rows:
tel = row[0]
tel = str(''.join(filter(str.isnumeric, tel)))
if len(tel) == 11:
ret = ret + tel + "\n"
elif len(tel) == 13:
ret = ret + "+" + tel + "\n"
return (ret)
app.run()
It's been 3 days since I started to use Python and Flask, so plz, no judgment :D
The flask request.files machinery assumes that the request is going to be a POST, because it isn't possible to upload a file via a GET request (you can GET the file's data, but that's not quite the same thing*).
Therefore, the form's method attribute should be POST, and it should also have the attribute enctype set to "multipart/form-data" for absolute correctness.
<form action="/api/listtels" method="POST" enctype="multipart/form-data">
On the server side
The route should accept POST requests only.
The names of file inputs don't form part of request.args, so the membership test should be changed to test request.files.
The uploaded file needs to be decoded and loaded into a buffer so that it can be handled by csv.reader (or it could be written to disk and then read back)
import io
#app.route('/api/listtels', methods=['POST'])
def ltels():
tels = "ERROR"
ret = "ERROR"
# Check if the file is loaded
if 'tels' in request.files:
tels = request.files['tels']
ret = "tel\n"
else:
return("\'tels\' arg is needed")
# Load decoded file data into a text buffer
buf = io.StringIO(tels.read().decode('utf-8'))
rows = csv.reader(buf,delimiter=",",lineterminator="\n")
next(rows, None)
for row in rows:
tel = row[0]
tel = str(''.join(filter(str.isnumeric, tel)))
if len(tel) == 11:
ret = ret + tel + "\n"
elif len(tel) == 13:
ret = ret + "+" + tel + "\n"
return (ret)
* You could get the content via GET either by pasting the file's contents into an <input type="textarea"...> or manipulating the form data with javascript before sending it to the server.
For Flask Admin How is it possible to upload images in Text area with editor ?
I need simpler version than this:
https://github.com/greyli/flask-ckeditor
from flask_ckeditor import *
csrf = CSRFProtect(app)
csrf.init_app(app)
ckeditor = CKEditor(app)
app.config['CKEDITOR_SERVE_LOCAL'] = False
app.config['CKEDITOR_HEIGHT'] = 400
app.config['CKEDITOR_FILE_UPLOADER'] = 'upload'
app.config['UPLOADED_PATH'] = os.path.join(basedir, 'uploads')
#app.route('/files/<filename>')
#csrf.exempt
def uploaded_files(filename):
path = app.config['UPLOADED_PATH']
return send_from_directory(path, filename)
import uuid
#app.route('/upload', methods=['POST'])
#csrf.exempt
def upload():
f = request.files.get('upload')
extension = f.filename.split('.')[1].lower()
if extension not in ['jpg', 'gif', 'png', 'jpeg']:
return upload_fail(message='Image only!')
unique_filename = str(uuid.uuid4())
f.filename = unique_filename + '.' + extension
f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
url = url_for('uploaded_files', filename=f.filename)
return upload_success(url=url)
in edit.html in flask admin
<script>
CKEDITOR.plugins.addExternal( 'filebrowser', '/static/ckeditor/filebrowser/', 'plugin.js' );
CKEDITOR.config.extraPlugins = 'filebrowser';
CKEDITOR.config.filebrowserBrowseUrl = '/upload';
<script>
This works for flask admin to upload images
https://github.com/greyli/flask-ckeditor I used this
I'm trying to take image input and do some processing on it before displaying the output on the webpage. However, when I try a different image, the older image is displayed instead of the new one.
Here are the code snippets:
from flask import Flask, render_template, request
import pro
app = Flask(__name__)
#app.route('/')
def index():
return render_template("index.html")
#app.route('/process', methods=['GET', 'POST'])
def process():
n = '/home/vivek/Desktop/CL3/d/booth/trial/uploads/up.jpg'
a = pro.pro(n)
return render_template("out.html")
if __name__ == '__main__':
app.run(debug=True)
The process code:
import cv2
def pro(p):
img = cv2.imread(p, 1)
# some process here
path = '/home/vivek/Desktop/CL3/d/booth/trial/static/'
cv2.imwrite(str(path) + 'out.jpg',img)
cv2.waitKey(0)
np = '/home/vivek/Desktop/CL3/d/booth/trial/static/out.jpg'
return np
And finally the HTML file:
<!DOCTYPE html>
<html>
<body>
<h2>OUTPUT</h2>
<img src="{{url_for('static', filename='out.jpg')}}" alt="OUTPUT" style="width:128px;height:128px;">
</body>
</html>
You need to disable cache
def process():
....
resp = make_response(render_template('out.html'))
resp.cache_control.no_cache = True
return resp
Updated
class MyFlask(Flask):
def get_send_file_max_age(self, name):
if name == 'out.jpg':
return 0
return Flask.get_send_file_max_age(self, name)
app = MyFlask(__name__)
i am writing an application in Flask and at some point I want to start some process (quick process) and then check if there is output on the server. If there is - download it, if not - show approprieate communicate.
Here is my code:
import os
import subprocess
import sqlite3
from flask import Flask, render_template, request, redirect, g, send_from_directory
app = Flask(__name__)
app.config.from_object(__name__)
# Config
app.config.update(dict(
DATABASE = os.path.join(app.root_path, 'database/records.db'),
DEBUG = True,
UPLOAD_FOLDER = 'uploads',
OUTPUT_FOLDER = 'outputs',
ALLOWED_EXTENSIONS = set(['txt', 'gro', 'doc', 'docx'])
))
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']
def run_calculations(filename):
subprocess.call(['python', os.path.join(app.root_path, 'topologia.py'), 'uploads/' + filename])
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(app.config['DATABASE'])
return db
#app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
#app.route('/')
def index():
return render_template('index.html')
#app.route('/outputs/<filename>')
def calculated_record(filename):
return send_from_directory(app.config['OUTPUT_FOLDER'], filename)
#app.route('/upload', methods = ['GET', 'POST'])
def upload():
with app.app_context():
cur = get_db().cursor()
cur.execute('INSERT INTO records(calculated) VALUES(0)')
file_id = cur.lastrowid
get_db().commit()
file = request.files['file']
if file and allowed_file(file.filename):
input_file = str(file_id) +'.'+file.filename.rsplit('.', 1)[1]
file.save(os.path.join(app.config['UPLOAD_FOLDER'], input_file))
run_calculations(input_file)
output_name = '/outputs/topologia_wynik' + str(file_id) + '.top'
if os.path.isfile(output_name):
return redirect(output_name)
else:
return 'Your file is beeing loaded'
else:
return "Something went wrong, check if your file is in right format ('txt', 'gro', 'doc', 'docx')"
if __name__ == '__main__':
app.run()
My whole problem is in this part of code:
if os.path.isfile(output_name):
return redirect(output_name)
else:
return 'Your file is beeing loaded'
Because if is never true... When I delete this part of code and redirect to output file without checking it all works fine... Do you have any idea why this happens?
The reason is probably / in the beginning of '/outputs/topologia_wynik' + str(file_id) + '.top'. It means "outputs" folder should be under the root folder, and in your case it seems to be under the server working directory.
Why not pass os.path.join(app.config['OUTPUT_FOLDER'], 'topologia_wynik' + str(file_id) + '.top') to os.path.isfile() as you did with the input file name?