Flask 下载中文名文件

本文介绍使用Flask框架实现实时生成并下载Excel文件的方法,包括如何利用xlsxwriter生成Excel,通过BytesIO发送文件流,并解决中文文件名的编码问题。

在写 flask 后端的时候,特别是在做数据相关的操作的时候,产品往往需要我们做一个导出数据的需求,一般都是导出 excel 格式的文件。

那在 flask 上,如何实现请求连接即可让浏览器下载呢?有两种思路。

一:

文件在本地磁盘,这时候我们只需要发送相应的地址过去即可。

二:

通过 io 中的 BytesIO, 把文件以二进制的形式发送过去,这里我们需要使用 flask 自带的 send_file。

第一种的坏处在于不便于权限控制,拿到下载链接在哪都能下载,第二种方法的缺陷在于只能接收 get 请求,post 请求发送的文件浏览器是不能识别的。

要实现 send_file, 是很容易的, 代码如下(适用于 python 3):

import xlsxwriter
from io import BytesIO
from flask import Flask, send_file

app = Flask(__name__)


@app.route("/download", methods=["GET"])
def download():
    out = BytesIO()
    workbook = xlsxwriter.Workbook(out)
    table = workbook.add_worksheet()
    table.write(0, 0, "name")
    table.write(0, 1, "age")
    workbook.close()
    out.seek(0)
    return send_file(out, as_attachment=True, attachment_filename="dream.xlsx")


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

这是一个完整的后端程序,能够直接跑起来。

其中我们用到了 xlsxwriter 这个库,用来生成一个 excel 文件, 直接传给 BytesIO() 成数据流的形式发出去,浏览器接收到这些数据流,回自动进行下载,文件名即是 send_file 参数中的 attachment_filename, 在我们这里便是 dream.xlsx 。

启动程序,在浏览器中输入 127.0.0.1:5000/download, 即可下载名为 dream.xlsx 的文件。

clipboard.png

我们打开看看:

clipboard.png

的确是我们生成的一个 excel 表格。

现在问题来了,这里的文件名是英文的,那我们需要中文怎么办?直接把 attachment_filename 参数改成 attachment_filename="测试表格.xlsx"可以么?

我们来试试:

return send_file(out, as_attachment=True, attachment_filename="测试表格.xlsx")

其余代码不变,仅有此处发成改变。

运行代码,浏览器访问下载试试。

浏览器没有任何反应,代表我们没有把数据流传给它,看程序,也报错了,报错信息:

UnicodeEncodeError: 'latin-1' codec can't encode characters in position 43-46: ordinal not in range(256)

编码问题。

解决办法如下:

import xlsxwriter
from io import BytesIO
from flask import Flask, send_file
from urllib.parse import quote

app = Flask(__name__)


@app.route("/download", methods=["GET"])
def download():
    out = BytesIO()
    workbook = xlsxwriter.Workbook(out)
    table = workbook.add_worksheet()
    table.write(0, 0, "name")
    table.write(0, 1, "age")
    workbook.close()
    out.seek(0)
    filename = quote("测试表格.xlsx")
    rv = send_file(out, as_attachment=True, attachment_filename=filename)
    rv.headers['Content-Disposition'] += "; filename*=utf-8''{}".format(filename)
    return rv


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

我们从 urllib.parse 引入 quote, 首先对文件名进行编码,然后 send_file 中 作为 attachment_filename 的参数,这时候能成功下载文件,但是文件名是编码后的名字,要解码的话,我们需要在 headers 里面声明编码格式,即:

rv.headers['Content-Disposition'] += "; filename*=utf-8''{}".format(filename)

这样的话,对文件名进行 UTF-8 解码,我们的文件名就是中文了。

如图:

clipboard.png

打开文件,也是我们想要的,如图:

clipboard.png

大功告成!

当然实际生产工作中,数据量是是非常大的,生成 excel 文件将会特别耗时,我们当然不希望我们的程序在此堵塞, 这时候我们可以使用 celery 异步任务,返回前端一个任务 ID, 前端去轮询这个任务 ID,当文件生成好了,即可开始下载。

由于有些时候我们 get 请求无法满足我们的参数传递,比如有多个嵌套对象作为参数传递,我们必须使用 post 请求,这时候同样可以采用 celery 异步任务的方式,返回任务 ID, 轮询任务状态,下载文件。

之后我会写一篇教程,celery 异步任务。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值