Flask 下載中文名文件

在寫 flask 後端的時候,特別是在作數據相關的操做的時候,產品每每須要咱們作一個導出數據的需求,通常都是導出 excel 格式的文件。前端

那在 flask 上,如何實現請求鏈接便可讓瀏覽器下載呢?有兩種思路。python

一:flask

文件在本地磁盤,這時候咱們只須要發送相應的地址過去便可。後端

二:瀏覽器

經過 io 中的 BytesIO, 把文件以二進制的形式發送過去,這裏咱們須要使用 flask 自帶的 send_file。app

第一種的壞處在於不便於權限控制,拿到下載連接在哪都能下載,第二種方法的缺陷在於只能接收 get 請求,post 請求發送的文件瀏覽器是不能識別的。異步

要實現 send_file, 是很容易的, 代碼以下(適用於 python 3):post

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 異步任務。

相關文章
相關標籤/搜索