1. 接口返回真實的文件
這種情況比較簡單, flask里帶有此類api, 可以用send_from_directory和send_file.
核心代碼如下:
from flask import send_file, send_from_directory
import os
@app.route("/download/<filename>", methods=['GET'])
def download_file(filename):
# 需要知道2個參數, 第1個參數是本地目錄的path, 第2個參數是文件名(帶擴展名)
directory = os.getcwd() # 假設在當前目錄
return send_from_directory(directory, filename, as_attachment=True)
后邊那個as_attachment參數需要賦值為True,不過此種辦法有個問題,就是當filename里邊出現中文的時候,會報如下錯誤:
解決辦法:
使用flask自帶的make_response
代碼修改如下
from flask import send_file, send_from_directory
import os
from flask import make_response
@app.route("/download/<filename>", methods=['GET'])
def download_file(filename):
# 需要知道2個參數, 第1個參數是本地目錄的path, 第2個參數是文件名(帶擴展名)
directory = os.getcwd() # 假設在當前目錄
response = make_response(
send_from_directory(excel_path, filename.encode('utf-8').decode('utf-8'), as_attachment=True))
response.headers["Content-Disposition"] = "attachment; filename={}".format(filename.encode().decode('latin-1'))
return response
使用make_response函數建立一個response對象,然后將filename編碼轉為latin-1,可以看到server.py里邊會嚴格按照latin-1編碼來解析filename,所以我這里的做法是先將utf8編碼的中文文件名默認轉為latin-1編碼。
2.接口返回文件數據流
這種情況比較適合我現在的需求,因為我這邊是用requests庫,先請求一個oss鏈接,獲取到文件的數據,然后我發現目前flask沒有這樣的api實現,這里還是使用make_response方法實現。
代碼如下:
import mimetypes
@app.route('/fileManager/download/<projId>/<id>/<filename>', methods=['GET'])
def download_file(projId, id, filename):
try:
url = "your url"
r = requests.get(url, timeout=500)
if r.status_code != 200:
raise Exception("Cannot connect with oss server or file is not existed")
response = make_response(r.content)
mime_type = mimetypes.guess_type(filename)[0]
response.headers['Content-Type'] = mime_type
response.headers['Content-Disposition'] = 'attachment; filename={}'.format(filename.encode().decode('latin-1'))
return response
except Exception as err:
print('download_file error: {}'.format(str(err)))
logging.exception(err)
return Utils.beop_response_error(msg='Download oss files failed!')
解釋一下:
make_response很強大,下載一個文件,需要在response的headers里邊添加一些信息,比如文件的類型,文件的名字,是否以附件形式添加,這3個是比較關鍵的信息。
mime_type是文件的類型,我觀察send_file的源代碼發現里邊用到了mimetypes.guess_type()這個方法,也就是猜測文件的類型,然后這里我就直接搬過來用了哈哈,r.content其實就是文件的數據流,之前我是通過
with open(filename, 'wb') as file:
file.write(r.content)
這樣實現下載文件到本地的,所以其實r.content是一個文件數據流,也不清楚我的名詞用的是否恰當哈哈。
之所以不用第一種方式,是因為我本地生成文件了之后,需要刪除他,但是刪除的時候總是會提示該文件已經被另一個程序使用,所以猜測是send_file這個api還在使用該文件,為了達到更好的效果,找到了第二種解決辦法。
其實還有一種解決辦法:
3. 發送靜態文件
from flask import app
import os
@app.route("/download/<filepath>", methods=['GET'])
def download_file(filepath):
# 此處的filepath是文件的路徑,但是文件必須存儲在static文件夾下, 比如images\test.jpg
return app.send_static_file(filepath)