Last active
May 28, 2021 00:36
-
-
Save ernstki/fbdde2f57665e299e02852beeae18dfe to your computer and use it in GitHub Desktop.
Minimum-viable Flask app that processes uploaded file input (and offers the result for download)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
## Take a file uploaded by the user, process it, and offer the result for | |
## download | |
## | |
## For a more complete example (with better input checking), see: | |
## https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/ | |
## | |
## Run like this: | |
## $ mkdir uploads | |
## $ FLASK_APP=flask-upload-download.py flask run | |
## | |
import os | |
import mimetypes | |
import subprocess | |
from flask import Flask, abort, redirect, url_for, request, render_template, \ | |
send_from_directory | |
from jinja2 import Template | |
from werkzeug.utils import secure_filename | |
# Flask configuration | |
MAX_CONTENT_LENGTH = 1024*1024 # 1 MB | |
inputpage = """<html> | |
<body> | |
<h1>Select a file to upload:</h1> | |
<form action="/wordcount" method="POST", enctype="multipart/form-data"> | |
<input type="file" name="inputfile"> | |
<br><br> | |
<input type="submit" value="Upload"> | |
</form> | |
</html>""" | |
outputpage = Template("""<html> | |
<body> | |
<h1>Your file, <tt>{{ filename }}</tt>, has {{ linecount }} lines.</h1> | |
<a href="/download/{{ filename }}">Download results for {{ filename }}</a> | |
</body> | |
</html>""") | |
errorpage = Template("""<html> | |
<body> | |
<h1>{{ error }}</h1> | |
<p>{{ detail }}</p> | |
</body> | |
</html>""") | |
app = Flask(__name__) | |
app.config.from_object(__name__) | |
@app.errorhandler(400) | |
def bad_request(detail): | |
return render_template(errorpage, error='Input Error', detail=detail) | |
@app.errorhandler(500) | |
def internal_error(detail): | |
return render_template(errorpage, error='Internal Error', detail=detail) | |
@app.route('/') | |
def index(): | |
return inputpage | |
@app.route('/wordcount', methods=['POST']) | |
def process_upload(): | |
infile = request.files['inputfile'] | |
# 'uploads' subdirectory must already exist | |
infilename = secure_filename(infile.filename) | |
inpath = os.path.join('uploads', infilename) | |
infile.save(inpath) | |
mimetype = mimetypes.guess_type(inpath) | |
if not mimetype[0]: | |
abort(400, f"Unrecognized MIME type; input file must be plain text.") | |
# quick sanity check to make sure it's a plain (uncompressed) text file | |
if not mimetype[0].startswith('text/') and mimetype[1] is None: | |
abort(400, f"Input file must be plain text (got {mimetype[0]}).") | |
try: | |
p = subprocess.run(['wc', '-l', inpath], check=True, | |
encoding='utf8', capture_output=True) | |
except subprocess.SubprocessError as e: | |
abort(500, f"Problem processing input file (<tt>{e.stderr}</tt>).") | |
# overwrite the input file with the output of 'wc -l' | |
with open(inpath, 'w') as outf: | |
outf.writelines(p.stdout) | |
return redirect(url_for('process_upload') + '/' + infilename) | |
@app.route('/wordcount/<filename>', methods=['GET']) | |
def display_result(filename): | |
filename = secure_filename(filename) | |
wcfile = os.path.join('uploads', filename) | |
if not os.path.isfile(wcfile): | |
abort(400, f"No such file <tt>{filename}</tt>.") | |
# read the linecount out of the file in 'uploads/' subdirectory | |
with open(wcfile, 'r') as f: | |
count = f.readline().strip().split(' ')[0] | |
return render_template(outputpage, filename=filename, linecount=count) | |
@app.route('/download/<filename>') | |
def download_result(filename): | |
filename = secure_filename(filename) | |
wcfile = os.path.join('uploads', filename) | |
if not os.path.isfile(wcfile): | |
abort(400, f"No such file <tt>{filename}</tt>.") | |
return send_from_directory('uploads', filename, as_attachment=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment