ISTI Vita
ISTI Vita

Reputation: 51

Flask - Calling python function that return image on button OnClick

I'm trying to make a really simple web application that take images inputted by the client, then process it on server and then return the image. I want the image to be displayed on one of the div. I tried to use ajax but it doesn't work. I'm very new to flask and ajax, found the example that return text.

This is my main.py

import os
#import magic
import urllib.request
from app import app
from flask import Flask, flash, request, redirect, render_template, send_file
from werkzeug.utils import secure_filename
from fungsi import plot

ALLOWED_EXTENSIONS = set(['txt', 'tif'])

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

@app.route('/')
def upload_form():
    return render_template('upload_.html')

@app.route('/', methods=['POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the files part
        if 'files[]' not in request.files:
            flash('No file part')
            return redirect(request.url)
        files = request.files.getlist('files[]')
        for file in files:
            if file and allowed_file(file.filename):
                filename = secure_filename(file.filename)
                file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
        flash('Files successfully uploaded')
        return redirect('/')

@app.route('/foo', methods=['GET'])
def foo():
    for root, dirs, files in os.walk(app.config['UPLOAD_FOLDER']):
        for file in files:
            if file.endswith('B4.TIF'):
                fname4 = os.path.join(app.config['UPLOAD_FOLDER'], file)
            if file.endswith('B5.TIF'):
                fname5 = os.path.join(app.config['UPLOAD_FOLDER'], file)

    bytes_obj = plot(fname4,fname5)

    return send_file(bytes_obj,
                     attachment_filename='plot.png',
                     mimetype='image/png')

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

html code

<!doctype html>
<html>
<title>Python Flask Multiple Files Upload Example</title>

<head><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> </head>


<body>

    <div class="row">
        <div class="col-sm-6">
            <h4>Plot</h4>
            <button id='myButton' type="button">Click Me!</button>
            <div id="img"></div>
        <div class="col-sm-6">
            <h2>Select file(s) to upload</h2>
            <p>
                {% with messages = get_flashed_messages() %}
                  {% if messages %}
                    <ul class=flashes>
                    {% for message in messages %}
                      <li>{{ message }}</li>
                    {% endfor %}
                    </ul>
                  {% endif %}
                {% endwith %}
            </p>
            <form method="post" action="/" enctype="multipart/form-data">
                <dl>
                    <p>
                        <input type="file" name="files[]" multiple="true" autocomplete="off" required>
                    </p>
                </dl>
                <p>
                    <input type="submit" value="Submit">
                </p>
            </form>
        </div>
    </div>

</body>
<script>
                $(document).ready(function() {
                $('#myButton').click(function() {
                    $.ajax({
                          type:"get",
                          url: "/foo",
                          dataType:"image/png"
                          success: function(response){
                           $("#img").html(response.html);
                          }
                  });
                });
                });
            </script>
</html>

Upvotes: 1

Views: 1462

Answers (1)

Tux1965
Tux1965

Reputation: 15

You first need to run to be able to make a flask form

pip install flask-wtf

once you do that, make a form file like this:

from flask_wtf import FlaskForm
from flask_wtf.file import SubmitField

class foo(FlaskForm):
    image=FileField('Images')
    submit=SubmitField('Submit')

then in your routes file. I haven't found a way to get around using an if statement to make sure the list is populated yet, but honestly I haven't really tried and it works. It will return a list of objects and by making sure the first one's filename isn't an empty string (representing no images were actually selected) then it should work. The python file should look like this.

from formFile import foo

@app.route('/whatever',methods=['POST'])
def bar():
    form=foo()
    images=[]
    if form.image.data[0].filename!='':
        images.append(image)
    return render_template('yourHTML.html',form=form,images=images)

I just added the extra import and didn't add all the imports relevant. Now to access each image you just need to run a simple for loop

for image in form.image.data:
    //for loop contents

with your html file's form part looking similar to this:

<form method="POST" action="" enctype="multipart/form-data">
    {{form.hidden_tag()}}
    <fieldset class="form">
        <legend class="legend">Upload Image</legend>
            <div class="scrolling-wrapper">
                <p class="form-control-label">Current Images</p>
                {%for image in images%}
                    <div class="card"><img src="/static/images/{{image}}" alt="Pic"/></div>
                {%endfor%}
            </div>
        <div class="form-group">
            {{ form.image.label() }}
            {{ form.image(class="form-control-file") }}
            {% if form.image.errors %}
                {% for error in form.image.errors %}
                    <span class="text-danger">{{ error }}</span></br>
                {% endfor %}
            {% endif %}
        </div>
        <div class="form-group">
            {{form.submit(class="btn btn-outline-info")}}
        </div>
    </fieldset>
</form>

where you don't need the error section. The classes here used are part of Bootstrap just to style so obviously you can replace them with your own styling if you'd prefer. The "scrolling-wrapper" class is a custom one just to have all the pictures in one line in a scrollable box. I can add that code of you'd like. That should be everything you need, and if I'm missing anything it might be because I used my own code that implements a saving function that saves the picture to the server and the specific image can be accessed by an SQL Database query. If I had to guess you could just use: src={{image}} instead of src="/static/images/products/{{image}}", but don't hold me to it as I haven't tested that. Also I didn't add any validators, but you know how to implement them anyways.

Upvotes: 1

Related Questions