FastApi下載檔案
記得之前我們講過生成excel檔案的事情,那麼如何把伺服器
生成的excel檔案正確傳送給使用者呢?
今天我們就來說說在FastApi中如何正確讓使用者下載到想要的檔案。
基本流程
其實檔案下載的場景還是挺多的,比如我想要拿到我這個使用者最近10天建立的測試用例資料,那麼我們服務端應該怎麼做呢?
- 根據條件篩選出正確的資料
- 處理資料,生成對應的目標格式檔案,比如csv,xlsx等等
- 返回http響應,其中
指定response的內容和型別
溫馨提示
現在假設我們已經完成了之前的步驟,並且生成了一個臨時檔案
。
需要注意的是,臨時檔案的名字為了確保唯一性,最好是用時間戳+隨機字串,或者懶一點可以用uuid
這裡為了方便,我就編寫一個簡便的方法:
import time
import random
random_str = list("abcdefgh")
random.shuffle(random_str)
filename = f"{time.time_ns()}_{''.join(random_str)}"
取當前時間戳(精確到納秒),這時候還是有可能會有同一請求發生,所以我們再用random.shuffle
對我們想要加的字串進行隨機排序(打亂順序)。
這樣一來,檔案重名的概率就小了非常多,如果要嚴謹的話,可以把字串放長點,但是檔名也會拖很長。
記得一定要儲存
這個隨機的檔名,並且加上檔案字尾哈~!
FastApi怎麼做呢
其實檔案也是HTTP的響應之一,只不過它相對特殊。
在FastApi中,響應有Response和FileResponse等多種,我們暫時看Response和FileResponse即可。
Response是我們常見的型別,當然fastapi比較友好,你如果return 一個字典,會預設將之轉換為JSON Response。
但當你要設定返回的http狀態碼,那就需要去操作這個Response物件
了。
我們常見的比如403 forbidden,401未認證,都可以用Response來實現。
那麼對於FileResponse,我們怎麼用呢?
其實用法比較簡單,我們來看實戰:
from fastapi import FastAPI
from starlette.responses import FileResponse
app = FastAPI(name="monitor")
@app.get("/download")
async def download():
# 處理完畢檔案以後,生成了檔案路徑
filename = "你要下載的檔案路徑.xls"
return FileResponse(
filename, # 這裡的檔名是你要傳送的檔名
filename="lol.exe", # 這裡的檔名是你要給使用者展示的下載的檔名,比如我這裡叫lol.exe
)
這樣,前端頁面提供一個a標籤,href地址填對應的介面地址就好了。
<!DOCTYPE html>
<html>
<head>
<title>測試</title>
</head>
<body>
<!-- 這個地址用你的host:port加上介面地址-->
<a href="http://localhost:7777/download">下載檔案</a>
</body>
</html>
等等 好像少了點啥
我們這個生成的檔案雖然說都是隨機的,沒啥影響。但是如果一直有人生成,那不刪除真的大丈夫嗎?
所以我們得考慮下怎麼刪除檔案~
-
理所當然認為try finally
答案是行不通的,因為
finally的內容會在return之前進行
。如果這時候你刪除了檔案,那麼Response就返回不了檔案了,會報錯。還好我們FastApi原生提供了background功能,幕後工作人員會在執行完畢之後進行一些
暗箱操作
。所以我們可以這麼改動:
from starlette.background import BackgroundTask
return FileResponse(
filename,
filename="application.xls",
background=BackgroundTask(lambda: os.remove(filename)),
)
使用background接受一個引數BackgroundTask,裡面引數是一個無參方法:
lambda: os.remove(filename)
也就是刪除這個檔案的方法。
最後
flask的相關檔案下載可以看博主幾年前寫的文章,原理都通用。