前段時間被某個前端小可愛鄙視了一下,說我博客都一年不更新了,我不服,明明還有倆月才到一年呢。不過說是這么說,還是要更新一下的。
以上都是借口,下面開始正文。
我公司的某個內部系統,用django做的,項目中不可避免地有下載文件的地方,以前偷懶,我都是用django自帶的方法,在項目的總urls.py中使用
urlpatterns += static(FILEPATH, document_root=FILEPATH)
這種方法解決。
但是這種方法有個極大的缺陷:測試環境寫着玩可以,正式環境肯定要把settings中的debug=True關掉的。而這種方法,在關掉debug之后,就不能用了。
於是我只好走傳統線路:設置header。
在urls.py中增加:
url(r"^download/$", views.download, name="download")
在頁面中:
<a href="{% url 'download' %}">下載</a>
在views.py中增加一個視圖函數:
def download(request): return build_download_response(FILE_PATH, "我愛可樂.docx")
(MTV模式的程咬金三板斧,沒毛病)
由於系統里面不止一個地方用到下載功能,所以,我把它寫成了一個通用的函數:
from django.http import FileResponse def build_download_response(filepath, filename): """ 構建下載文件的文件頭 :param filepath: 文件路徑 :param filename: 文件名 :return: FileResponse """ absname = os.path.join(filepath, filename) if os.path.isdir(filepath) else filepath response = FileResponse(open(absname, "rb")) response["Content-Type"] = "application/octet-stream" response["Content-Disposition"] = "attachment; filename='%s'" % filename return response
參照網上的說法,這樣是沒問題的。藍鵝,它確實出了問題:當我點擊下載按鈕的時候,彈出的界面只有“下載”二字。

喵喵喵?我的文件呢?
我嘗試把文件下載下來看了一下,從大小來看,是沒有問題的。

我把名字改過來,可以正常打開,里面沒有丟東西——換句話說,
只是文件名出了問題。
可是文件名能有毛的問題啊?你不能欺負我公司的人只會中文吧!

經過一番激烈地百度和扣人心弦地搜索,我從這篇博客
https://blog.csdn.net/u011090495/article/details/18815777 里找到了原因。
簡單點說就是,
Content-Disposition里面的filename這個東西,原來不是RFC標准,僅支持ASCII編碼的文件名。如果文件名不是英文的,恐怕就會出現名字亂碼,或者被改名的情況。那么怎么辦呢?另一個RFC給加上了擴展,可以定義一個filename*,然后把文件名編碼一下。
於是,我把 Content-Disposition 這里從
response["Content-Disposition"] = "attachment; filename='%s'" % filename
改成了
response["Content-Disposition"] = "attachment; filename='%s'; filename*=UTF-8''%s" % (filename.encode("UTF-8"), filename.encode("UTF-8"))
結果測試了一下,哭了。

還真是編碼了……
我要鬧了。
還好,后來又在
https://segmentfault.com/q/1010000009078463 這篇帖子里找到了答案。原來 filename*=UTF-8''%s" % filename.encode("UTF-8") 這樣寫是不管用的,django自有django的方法:
from django.http import FileResponse from django.utils.encoding import escape_uri_path def build_download_response(filepath, filename): """ 構建下載文件的文件頭 :param filepath: 文件路徑 :param filename: 文件名 :return: FileResponse """ absname = os.path.join(filepath, filename) if os.path.isdir(filepath) else filepath response = FileResponse(open(absname, "rb")) response["Content-Type"] = "application/octet-stream" response["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(escape_uri_path(filename)) return response
於是,就可以正常下載了。

打開看看,內容完全沒問題!

開心!(我才不會拿去給前端炫耀呢!)