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"
Related
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>
I have a CSV file called random.csv which I want to render on a html page if the user is logged in. I've tried using tablib for this.
__init__.py
from flask import Flask
import tablib
app = Flask(__name__)
dataset = tablib.Dataset()
with open(os.path.join(os.path.dirname(__file__), 'random.csv')) as f:
dataset.csv = f.read()
routes.py
#app.route('/dataset', methods=['GET', 'POST'])
#login_required
def dataset():
return dataset.html
This is the index.html file from where I want to link to the html page for the csv file.
{% extends "base.html" %}
{% block content %}
<p>Click to see CSV </p>
{% endblock %}
And this is the dataset.html file where I want to see the CSV data.
{% extends "base.html" %}
{% block content %}
{% endblock %}
I'm getting this error:
AttributeError: 'function' object has no attribute 'html'
The error is on the line in routes.py file where I return the dataset.html file.
I solved it by using pandas instead tablib. In my routes.py file just did this:
import pandas as pd
#app.route('/dataset', methods=['GET', 'POST'])
#login_required
def dataset():
table = pd.read_csv('filepath', encoding= 'unicode_escape')
return render_template("dataset.html", data=table.to_html())
The reason is that you shadowed whatever the variable was with the function name:
def dataset():
return dataset.html
dataset is the function. If it was something else before, naming your function dataset replaced whatever that variable was.
I am creating a piece of code which will take a csv file and upload the content of the file to a mysql database.
I am conducting this process with Flask, Python 2.7 and MySQL-python==1.2.5.
Currently I have a script (shown below) which fulfils this task, however I can only get the csv file to read/ upload the first row of the csv file.
Any help would be much appreciated.
CSVFILE:
date_time,first_name,surname,address,email
2018/03/23 12:09:10,Steve,Jones,UK,SteveJones#hotmail.com
2018/03/23 12:12:20,Jack,Flipper,UK,JackFlipper#hotmail.com
2018/03/23 12:14:30,Rebecca,Fletcher,France,RebeccaFletcher#hotmail.com
2018/03/23 12:15:10,Aby,Jones,Italy,AbyJones#hotmail.com
PYTHON:
import os
import io
import csv
from flask import render_template, request, redirect, url_for,flash
from flask_login import current_user, login_required
from flask_sqlalchemy import SQLAlchemy
from flask.ext.uploads import UploadSet, configure_uploads, DATA
from werkzeug.utils import secure_filename
from . import upload_csv_blueprint
from .. import db
from .. models import details
#upload_csv_blueprint.route('/upload_csv_layout', methods=['POST' , 'GET'])
#login_required
def upload_csv_layout():
return render_template('upload_csv.html', title='upload_csv')
#upload_csv_blueprint.route('/upload_csv', methods=['GET','POST'])
def upload_file():
if request.method == 'POST':
csvfile = request.files['file']
reader = csv.DictReader(csvfile)
data = [row for row in reader]
for row in data:
date_time_store = row['date_time']
first_name_store = row['first_name']
surname_store = row['surname']
address_store = row['address']
email_store = row['email']
query = details(date_time = date_time_store,first_name=first_name_store,surname=surname_store,address=address_store,email=email_store)
db.session.add(query)
db.session.commit()
return('Did it work?')
else:
return redirect(url_for('upload_csv.upload_csv_layout'))
HTML OF PAGE:
{% import 'bootstrap/utils.html' as utils %}
{% import 'bootstrap/wtf.html' as wtf %}
{% extends '/advanced_banner.html' %}
{% block body %}
<title> Upload CSV </title>
<form method=POST enctype=multipart/form-data action='/upload_csv'>
<input type='file' name='file' class='UCSV_FileInput'></input>
<button type='submit' value='Upload'>Submit</button>
</form>
</div>
<div class='flash_holding_box'>
<div class='flash'><b> Action Log </b> <br>{{utils.flashed_messages()}}</div>
</div>
{% endblock %}
Understand that my imports are a bit wacky, but been trying a few different techniques.
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 }}"
Here is my code for multiple files upload:
HTML CODE:
Browse <input type="file" name="pro_attachment1" id="pro_attachment1" multiple>
PYTHON CODE:
pro_attachment = request.files.getlist('pro_attachment1')
for upload in pro_attachment:
filename = upload.filename.rsplit("/")[0]
destination = os.path.join(application.config['UPLOAD_FOLDER'], filename)
print "Accept incoming file:", filename
print "Save it to:", destination
upload.save(destination)
But it uploads a single file instead of multiple files.
How to
In the template, you need to add mulitple attribute in upload input:
<form method="POST" enctype="multipart/form-data">
<input type="file" name="photos" multiple>
<input type="submit" value="Submit">
</form>
Then in view function, the uploaded files can get as a list through request.files.getlist('photos'). Loop this list and call save() method on each item (werkzeug.datastructures.FileStorage) will save them at given path:
import os
from flask import Flask, request, render_template, redirect
app = Flask(__name__)
app.config['UPLOAD_PATH'] = '/the/path/to/save'
#app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and 'photo' in request.files:
for f in request.files.getlist('photo'):
f.save(os.path.join(app.config['UPLOAD_PATH'], f.filename))
return 'Upload completed.'
return render_template('upload.html')
Furthermore, you may need to use secure_filename() to clean filename:
# ...
from werkzeug.utils import secure_filename
# ...
for f in request.files.getlist('photo'):
filename = secure_filename(f.filename)
f.save(os.path.join(app.config['UPLOAD_PATH'], filename))
# ...
You can also generate a random filename with this method.
Full demo
View:
import os
from flask import Flask, request, render_template
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['UPLOAD_PATH'] = '/the/path/to/save'
#main.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit() and 'photo' in request.files:
for f in request.files.getlist('photo'):
filename = secure_filename(f.filename)
f.save(os.path.join(app.config['UPLOAD_PATH'], filename))
return 'Upload completed.'
return render_template('upload.html', form=form)
Form:
from flask_wtf import FlaskForm
from wtforms import SubmitField
from flask_wtf.file import FileField, FileAllowed, FileRequired
class UploadForm(FlaskForm):
photo = FileField('Image', validators=[
FileRequired(),
FileAllowed(photos, 'Image only!')
])
submit = SubmitField('Submit')
Template:
<form method="POST" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{{ form.photo(multiple="multiple") }}
{{ form.submit }}
</form>
More
For better upload experience, you can try Flask-Dropzone.
Your code looks perfect.
I think the only mistake your making is splitting and taking the first value.
And also i dont know about the rsplit(), but the split() works perfect for me.
HTML CODE
<input id="upload_img" name="zip_folder" type="file" multiple webkitdirectory >
PYTHON CODE
#app.route('/zipped',methods = ['GET', 'POST'])
def zipped():
if request.method == 'POST':
f = request.files.getlist("zip_folder")
print f
for zipfile in f:
filename = zipfile.filename.split('/')[1]
print zipfile.filename.split('/')[1]
zipfile.save(os.path.join(app.config['ZIPPED_FILE'], filename))
return render_template('final.html')