Flask WTForms 应用程序创建要下载的文件。用户获取其他人的文件(同名)。怎么解决?

问题描述 投票:0回答:1

我的应用程序根据 WTForms 上的输入在服务器中创建一个文件。文件名永远不会改变。当新用户进来时,填写表格,旧文件将被覆盖。问题是当 2 个用户碰巧同时使用该应用程序并获得错误的文件时(创建文件和下载文件之间的时间有时可能是 1 或 2 分钟)。 我想避免这个问题。 这是我的代码和文件夹结构:

  • Flask应用程序

    • 模块

         FileCreate.py
      
         Forms.py   
      
    • 模板

         InputPage.html
      
    • WebApp.py

WebApp.py:

from flask import Flask, render_template, send_file, request
from modules import Forms, FileCreate
app = Flask(__name__)

@app.route("/index", methods=["GET", "POST"])
def vFileCreate():
    form = Forms.clInputFromUser()
    if form.validate_on_submit() and request.method == "POST":
        listdata = form.stringdata.data
        userdata = FileCreate.clFileCreate(listdata, form)
        userdata.file_save()
        return render_template("InputPage.html", form=form, userdata=userdata)
    else:
        return render_template("InputPage.html")
if __name__ == '__main__':
   app.run(debug=True)

@app.route("/download_file")
def vDownloadFile():
    PATH = "UserReport.txt"
    return send_file(PATH, as_attachment=True, download_name="Sample_Report.txt")

Forms.py:

from flask_wtf import FlaskForm

class clInputFromUser(FlaskForm):
    stringdata = TextAreaField("Paste Column of Data Here: ")
    submit = SubmitField("Analyze Data and Create File")

文件创建.py:

class clFileCreate:
    def __init__(self, listdata, form):
        self.formdata = form
        self.data = formdata.stringdata.data
        fp = open('UserReport.txt', 'w')
        fp.write(self.data)
        fp.close()

InputPage.html:

<!DOCTYPE html>
<html>
<head>
    <title>Form Page</title>
</head>
<body>
    <form method="POST">
        {{ form.hidden_tag() }}
        <div>
            {{ form.stringdata.label }}<br>
            {{ form.stringdata(size=32) }}
        </div>
        <div>
            {{ form.submit() }}
        </div>
    </form>
    <a href="{{ url_for('vDownloadFile') }}"><button type="button" class="btn btn-success">Download Report</button></a>
</body>
</html>

我必须采取哪些选项来确保向刚刚填写表单的用户提供正确的文件。 由于限制,没有数据库,服务器上没有 CRON 作业来清理文件,无法识别用户。

Idea1:我可以在下载之前重新运行该课程,但这需要再次提交表单,我不知道如何进行该操作。

Idea2:我可以流式传输文件,但我不知道如何将流变量移动到 download_file 路径中的下载函数,因为它们是两个不同的函数。

Idea3:我可以识别会话,我不知道这是否可能,并通过使文件名对会话唯一来确保用户获得正确的文件,但我不知道之后如何删除该文件从类中下载或将唯一的文件名传递给 download_file 函数。 (参见限制)。

有什么想法吗?

注意:此代码是显示结构的示例。我没有测试它,因为问题更多的是关于如何解决问题的概念,而不是具体的代码。

python flask download jinja2 wtforms
1个回答
0
投票

也许 APScheduler 是使用 cron 作业的替代方案。

以下示例创建一个用户文件夹,其唯一名称存储在会话存储中。调度程序每小时都会删除一个多小时内未修改的所有文件夹。因此,数据可以分配给每个匿名用户,并且可以由他们在至少一小时内下载。不需要数据库。

from apscheduler.schedulers.background import BackgroundScheduler
from flask import (
    Flask, 
    redirect, 
    render_template, 
    request,
    send_from_directory,  
    session, 
    url_for
)
from flask_wtf import FlaskForm
from wtforms import SubmitField, TextAreaField
from wtforms.validators import Length
import os, shutil, time, uuid

app = Flask(__name__)
app.config.from_mapping(
    SECRET_KEY='your secret here', 
    UPLOAD_FOLDER=os.path.join(app.instance_path, 'uploads')
)

os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

def prune(path, hours=1):
    now = time.time()
    for f in os.listdir(path):
        p = os.path.join(path, f)
        if os.stat(p).st_mtime < now - hours*60*60:
            shutil.rmtree(p, ignore_errors=True)

sched = BackgroundScheduler(daemon=True)
sched.add_job(prune, trigger='interval', args=[app.config['UPLOAD_FOLDER']], hours=1)
sched.start()

class ExampleForm(FlaskForm):
    content = TextAreaField('Your data here:', 
        validators=[
            Length(min=4, max=32)
        ]
    )
    submit = SubmitField('Analyze')

@app.route('/', methods=['GET', 'POST'])
def index():
    if not 'uid' in session:
        session['uid'] = str(uuid.uuid4())

    filepath = os.path.join(app.config['UPLOAD_FOLDER'], session['uid'])

    form = ExampleForm(request.form)
    if form.validate_on_submit():
        os.makedirs(filepath, exist_ok=True)

        filename = 'UserReport.txt'
        with open(os.path.join(filepath, filename), 'w') as f:
            f.write(form.content.data)

        return redirect(request.url)

    files = os.listdir(filepath) if os.path.exists(filepath) else [] 
    return render_template('index.html', **locals())

@app.route('/download/<path:filename>')
def download(filename):
    return send_from_directory(
        os.path.join(app.config['UPLOAD_FOLDER'], session.get('uid')), 
        filename, 
        as_attachment=True
    )
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Index</title>
</head>
<body>
    <div>
        <form method="POST">
            {{ form.csrf_token }}
            <div>
                {{ form.content.label() }}
                {{ form.content() }}
                {% if form.content.errors -%}
                    <ul>
                        {% for error in form.content.errors -%}
                        <li>{{ error }}</li>
                        {% endfor -%}
                    </ul>
                {% endif -%}
            </div>
            {{ form.submit() }}
        </form>
    </div>

    <div>
        <ul>
            {% for filename in files -%}
            <li><a href="{{ url_for('download', filename=filename) }}" target="_blank">{{ filename }}</a></li>
            {% endfor -%}
        </ul>
    </div>
</body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.