1

I am building a small Flask app in Windows 11. In my app, I take a string from a user, convert it to a text file, and send the user that text file in a subfolder of 'static'. However, the key thing I want to do is send that file as an anchor tag that downloads on clicking.

I want the download link to appear as a clickable link on the original webpage (that is have a one page site that doesn't reroute), and I do not want to send a direct download (Flask's send_file function).

My Flask App (app.py):

#Package Imports
from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
import random

#Create Flask Form
#Create the Flask form here.
class MyForm(FlaskForm):
    text_field = StringField('Write here.', validators=[DataRequired()])
    submit_field = SubmitField('Submit')

#This function makes a text file with the user's text.
def make_text_file(my_string):
    filename = 'static/text_files/{}.txt'.format(random.randint(1, 1000000))
    with open(filename, 'w') as file:
        file.write(my_string)
    return '/'.join(filename.split('/')[1:])

#Create the Flask app here.
app = Flask(__name__)
app.config['SECRET_KEY'] = 'ABCDEFG'

@app.route('/', methods=['GET', 'POST'])
def index():
    result = False
    form = MyForm()
    
    if request.method == 'POST':
        text = request.form['text_field']
        result = make_text_file(text)
        return render_template('index.html', form=form, result=result)
    
    return render_template('index.html', form=form, result=result)

My HTML Jinja2/Template (index.html):

<html>
    <body>
        <form method="POST" action="{{ url_for('index') }}">
            <div class='form-div'>
                {{ form.csrf_token }}
                <div>{{ form.text_field }}</div>
                <div>{{ form.submit_field }}</div>
            </div>
        </form>
        
        <div id='result-link'>
            {% if result == False %}
                <div></div>
            {% else %}
                <a href="{{ url_for('static', filename=result) }}" download>Here is your file.</a>
            {% endif %}
        </div>
    </body>
</html>

I have tried the following solutions but they did not work:

  1. I used the @after_this_request decorator from Flask, but since my route function handles both 'GET' and 'POST' requests, how do I specify that a specific function (namely deleting the file) should happen after a 'POST' request? To my knowledge, this solution only works in Linux. While I do plan on serving the end website in a Linux server, is there a way to test the filed deletion mechanism in Windows?
  2. I tried WebSockets, namely Flask-SocketIO, and incorporated this into my code as well. However, I receive a "The WebSocket transport is not available, you must install a WebSocket server that is compatible with your async mode to enable it. See the documentation for details. (further occurrences of this error will be logged with level INFO)". I am running a Flask development server in the virtual environment and am unsure how to set up a production server to test whether the WebSocket strategy works.
  3. While the example I provide is text files, I eventually want to be able to serve video files to clients. Would reading the file into memory, per the solution here be a feasible solution?
Howsikan
  • 75
  • 7

1 Answers1

1

Example for sending images without saving to file

# define a predict function as an endpoint
@app.route("/predict", methods=["POST"])
def process_image():
    if request.method == 'POST':
        file = request.files['file']
        if file is not None:
            img_low = open_image(file)
            img_size = toEven(img_low.size)
            data_gen = get_dummy_databunch(1, img_size)
            learner.data = data_gen
            p,img_hr,b = learner.predict(img_low)
            img_bytes = BytesIO()
            img_hr_clipped = torch.clamp(img_hr, 0, 255)
            transform = T.ToPILImage()
            myimg = transform(img_hr_clipped)
            myimg.save(img_bytes, 'jpeg')
            img_bytes.seek(0)
            callback = send_file(img_bytes, mimetype='image/jpeg')
            return callback, 200
trace
  • 11
  • 4
  • Thank you for this. Is it possible to do this even in situations where I'm not using send_file() but calling the file's path in an anchor tag in a template? Also, how much memory does streaming the file into RAM take? Would it overload a server if there are multiple users? Thank you. – Howsikan Apr 25 '23 at 15:38