前言
由于最近在做檔案管理子產品的功能,是以難免會遇到檔案上傳下載下傳這塊的功能。不過檔案上傳那塊是調用的OSS api,是以接觸的不多。
檔案的下載下傳:
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(directory, filename, as_attachment=True))
response.headers["Content-Disposition"] = "attachment; filename={}".format(file_name.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. 發送靜态檔案
其實原來和第一種差不多,調用的api不一樣,api是
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)