因爲最近在作文件管理模塊的功能,因此不免會遇到文件上傳下載這塊的功能。不過文件上傳那塊是調用的OSS api,因此接觸的很少。python
這種狀況比較簡單, flask裏帶有此類api, 能夠用send_from_directory和send_file.flask
核心代碼以下:api
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裏邊出現中文的時候,會報以下錯誤:app
解決辦法:
使用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編碼。編碼
這種狀況比較適合我如今的需求,由於我這邊是用requests庫,先請求一個oss連接,獲取到文件的數據,而後我發現目前flask沒有這樣的api實現,這裏仍是使用make_response方法實現。url
代碼以下:code
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其實就是文件的數據流,以前我是經過orm
with open(filename, 'wb') as file: file.write(r.content)
這樣實現下載文件到本地的,因此其實r.content是一個文件數據流,也不清楚個人名詞用的是否恰當哈哈。server
之因此不用第一種方式,是由於我本地生成文件了以後,須要刪除他,可是刪除的時候老是會提示該文件已經被另外一個程序使用,因此猜想是send_file這個api還在使用該文件,爲了達到更好的效果,找到了第二種解決辦法。
其實還有一種解決辦法:
其實原來和第一種差很少,調用的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)