26

I am using flask, and trying to do something very simple using the quickstart tutorial, just running on my machine (local server). I produce a simple upload form which successfully uploads an image file. I then want to pass this image as a variable to a template.html for display within a page. The template.html file displays fine, but the image is always a broken link image symbol. I've tried a number of different paths, but I have a feeling I am doing things a bit wrong.

import os
from flask import Flask, request, redirect, url_for, send_from_directory, 
                  render_template

UPLOAD_FOLDER = '/home/me/Desktop/projects/flask/uploads'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return redirect(url_for('uploaded_file', filename=filename))
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form action="" method=post enctype=multipart/form-data>
      <p><input type=file name=file>
         <input type=submit value=Upload>
    </form>
    '''

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    filename = 'http://127.0.0.1:5000/uploads/' + filename
    return render_template('template.html', filename = filename)

if __name__ == '__main__':
    app.run()

This is template.html:

<!doctype html>
<title>Hello from Flask</title>
{% if filename %}
  <h1>some text<img src="{{filename}}"> more text!</h1>
{% else %}
  <h1>no image for whatever reason</h1>
{% endif %}

How can I pass the uploaded image file to template.html so it will display correctly?

Thanks

fraxel
  • 34,470
  • 11
  • 98
  • 102

3 Answers3

27

What's happening now is that /uploads/foo.jpg returns the HTML inside template.html. There you try to use /uploads/foo.jpg as the source of the img tag. Nowhere you serve the actual image out.

Let's modify it like this: /show/foo.jpg returns the HTML page and and /uploads/foo.jpg returns the image. Replace the latter route with these two and you should be good to go:

@app.route('/show/<filename>')
def uploaded_file(filename):
    filename = 'http://127.0.0.1:5000/uploads/' + filename
    return render_template('template.html', filename=filename)

@app.route('/uploads/<filename>')
def send_file(filename):
    return send_from_directory(UPLOAD_FOLDER, filename)
Miikka
  • 4,573
  • 34
  • 47
  • 1
    Perhaps this is a silly question but when will send_file every end up getting called with this addition? – GregarityNow Feb 10 '17 at 00:00
  • 1
    @Uzebeckatrente, the `` tag in the template points to `/uploads/`, which is routed to send_file. – Miikka Feb 10 '17 at 07:02
  • Hi @Miikka I am trying this on cloud server. The same logic doesnot work for me. It shows full image path inside src tag. Can you please suggest anything ?? – Akash Goyal Oct 31 '17 at 04:34
12

From the uploaded_file function, we head to the template.html and there will are redirected back <img src="{{ url_for('send_file', filename=filename) }}"> coming back we hit the send_file function which will show the content of the HTML inside the template with image uploaded and stored in the UPLOAD_FOLDER specified. you are also missing from werkzeug import secure_filename this in py file

@app.route('/show/<filename>')
def uploaded_file(filename):
    return render_template('template.html', filename=filename)

@app.route('/uploads/<filename>')
def send_file(filename):
    return send_from_directory(UPLOAD_FOLDER, filename)

Now your template.html will look like this..

<!doctype html>
<title>Hello from Flask</title>
{% if filename %}
  <h1>some text <img src="{{ url_for('send_file', filename=filename) }}">more text!</h1>
{% else %}
  <h1>no image for whatever reason</h1>
{% endif %}
Transformer
  • 3,642
  • 1
  • 22
  • 33
  • This if statement is not possible to find on the flask website, as least I couldn't find it. Only the session stuff is clear in the if statement examples. The part above relating to template.html is really important for displaying a placeholder image when no image is currently uploaded. That why I've given it a +1. – Eamonn Kenny May 10 '18 at 10:34
  • The if statement is correct, check this out http://flask.pocoo.org/docs/0.12/tutorial/templates/ – Transformer May 11 '18 at 12:51
  • 2
    I'm finding the flask manual impossible to follow. I've been coding over 10 years in python, 30 years in programming and have never seen such appalling documentation. I've tried the examples of 3-4 different pages and none of the syntax is correct. use of functions with same name for instance doesn't work in flask 0.12.4. filename doesn't actually represent file path on disk and getting access to file paths requires a second path if using absolute file paths. None of this is in the documentation. hours of debugging and guess work. – Eamonn Kenny May 11 '18 at 13:59
  • 1
    I read this comment accidentially but you are perfectly right: Flask documentation is horrible, best to be learnt from code snippets and answers from stackoverflow. It only gets worse combining Flask with javascript and deploying with docker, then errors are close to not solvable. – CLpragmatics Jun 04 '19 at 14:11
2

In case you just want to upload, analyze, and display the image without saving it to storage, i.e. api and edged image analysis, this example might be a guide for you. Flaskimio

@app.route("/",methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        if 'file' not in request.files:
            print('No file attached in request')
            return redirect(request.url)
        file = request.files['file']
        if file.filename == '':
            print('No file selected')
            return redirect(request.url)
        if file and check_allowed_file(file.filename):
            filename = secure_filename(file.filename)
            print(filename)
            img = Image.open(file.stream)
            with BytesIO() as buf:
                img.save(buf, 'jpeg')
                image_bytes = buf.getvalue()
            encoded_string = base64.b64encode(image_bytes).decode()         
        return render_template('index.html', img_data=encoded_string), 200
    else:
        return render_template('index.html', img_data=""), 200


<form method="post" enctype="multipart/form-data">
    <p><input type="file" id="file" name="file"><input type=submit value=Upload></p>
    <img src="data:image/jpeg;base64,{{ img_data }}" id="img-upload" alt="img_data" class="img-upload"/>
</form>
Nonsakhoo
  • 41
  • 3