Flask下载文件

116

我正在尝试使用Flask创建一个Web应用程序,允许用户上传文件并将其提供给另一个用户。现在,我可以正确地将文件上传到upload_folder中。但是,我似乎找不到让用户下载它的方法。

我将文件名存储在数据库中。

我有一个视图服务于数据库对象。我也可以删除它们。

@app.route('/dashboard', methods=['GET', 'POST'])
def dashboard():

    problemes = Probleme.query.all()

    if 'user' not in session:
        return redirect(url_for('login'))

    if request.method == 'POST':
        delete = Probleme.query.filter_by(id=request.form['del_button']).first()
        db.session.delete(delete)
        db.session.commit()
        return redirect(url_for('dashboard'))

    return render_template('dashboard.html', problemes=problemes)

在我的HTML中,我有:

<td><a href="{{ url_for('download', filename=probleme.facture) }}">Facture</a></td>

以及一个下载视图:

@app.route('/uploads/<path:filename>', methods=['GET', 'POST'])
def download(filename):
    return send_from_directory(directory=app.config['UPLOAD_FOLDER'], filename=filename)

但它返回:

未找到

服务器上未找到请求的 URL。如果您手动输入了 URL,请检查拼写并重试。

我只想将文件名链接到对象,让用户下载它(对于同一视图中的每个对象)


1
Flask返回404未找到响应的URL是什么?您是否验证了问题文件确实驻留在app.config ['UPLOAD_FOLDER']中? - Martijn Pieters
1
我问的原因是因为当没有文件时,send_from_directory() 会引发 NotFound 异常。所以 os.path.isfile(app.config['UPLOAD_FOLDER']/filename) 返回了 false。您需要仔细检查 UPLOAD_FOLDER 的内容,并验证您尝试加载的文件名是否确实存在。 - Martijn Pieters
是的,我不太明白,但它能工作... 我会保持这种方式 :S - Saimu
我不确定这是一个好主意;现在你正在将静态资源与上传文件混合在一起。 - Martijn Pieters
1
Python 处理尾部斜杠没问题。这里的问题是 前导 斜杠。你定义了一个绝对路径,而不是相对路径。 - Martijn Pieters
显示剩余5条评论
5个回答

104
你需要确保将传递给directory参数的值是一个绝对路径,根据你的应用程序的当前位置进行修正。
最好的方法是将UPLOAD_FOLDER配置为相对路径(没有前导斜杠),然后通过在前面添加current_app.root_path来使其变为绝对路径。
@app.route('/uploads/<path:filename>', methods=['GET', 'POST'])
def download(filename):
    uploads = os.path.join(current_app.root_path, app.config['UPLOAD_FOLDER'])
    return send_from_directory(uploads, filename)

重申一下,UPLOAD_FOLDER 必须是相对路径才能正常工作,例如不能以 / 开头。
相对路径可能可行,但太过依赖当前工作目录设置为 Flask 代码所在的位置。这并不总是成立。

Flask 2.0使用path=替换了必需的filename=参数。我怀疑新的可选文件名参数是客户端名称,但是文档没有说明。双眼转动表情。 - Bob Stein
1
@BobStein:filename参数已经完全消失了,但它只存在于2.0.x和2.1.x版本的发布中,用于触发警告(因此,如果您使用它,您会看到一个消息,建议现在使用path=)。您可以在任何版本中将名称作为位置参数使用。 - Martijn Pieters

97

在flask中下载文件的调用方法。文件名为Examples.pdf。当我访问127.0.0.1:5000/download时,应该开始下载。

示例:

from flask import Flask
from flask import send_file
app = Flask(__name__)

@app.route('/download')
def downloadFile ():
    #For windows you need to use drive name [ex: F:/Example.pdf]
    path = "/Examples.pdf"
    return send_file(path, as_attachment=True)

if __name__ == '__main__':
    app.run(port=5000,debug=True) 

3
这并没有解答问题,因为问题使用了一个可变路径元素来命名要下载的文件。 - Martijn Pieters
32
没关系。我认为应该更加通用地回答具有高SEO排名的“Flask下载文件”的问题。 - Jossef Harush Kadouri
2
简单而美丽,正是我所需要的。谢谢! - Eliezer Berlin
我有一种印象,send_from_directory()可能比send_file()更安全。 - Bob Stein

8
我也在开发类似的应用程序,即使文件存在,我也遇到了找不到错误。这解决了我的问题。我在“static_folder”中提到了我的下载文件夹:
app = Flask(__name__,static_folder='pdf')

我用来下载的代码如下:

@app.route('/pdf/<path:filename>', methods=['GET', 'POST'])
def download(filename):    
    return send_from_directory(directory='pdf', filename=filename)

这是我从HTML中调用文件的方式。
<a class="label label-primary" href=/pdf/{{  post.hashVal }}.pdf target="_blank"  style="margin-right: 5px;">Download pdf </a>
<a class="label label-primary" href=/pdf/{{  post.hashVal }}.png target="_blank"  style="margin-right: 5px;">Download png </a>

4
您不需要甚至想要static_folder设置为指向您的pdf子目录。这只意味着从/ static /路径中的所有内容也会从pdf子文件夹中提供服务。正如我在回答中所述:*相对路径可能有效,但过于依赖当前工作目录设置为Flask代码所在的位置;您在directory ='pdf'中的相对路径也将陷入其中。 - Martijn Pieters

8
#HTML Code
 
<ul>
 {% for file in files %}
  <li> <a href="{{ url_for('download', filename=file) }}">{{ file }}</a></li>
 {% endfor %}
</ul>


#Python Code

from flask import send_from_directory

app.config['UPLOAD_FOLDER']='logs'


@app.route('/uploads/<path:filename>', methods=['GET', 'POST'])
def download(filename):
    print(app.root_path)
    full_path = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'])
    print(full_path)
    return send_from_directory(full_path, filename)


0
尝试这样做(假设UPLOAD_FOLDER相对于您的工作目录)。
@app.route('/uploads/<path:filename>', methods=['GET', 'POST'])
def download(filename):
    path = os.path.join(os.getcwd(), app.config['UPLOAD_FOLDER'])
    return send_from_directory(directory=path, filename=filename)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接