Fastapi簡單實現臨時下載鏈接

我的微信公衆號提供了文件下載功能,即 wine 安裝微信時需要的一個文件InstMsiW.exe。上次是寫死路徑放在 static 目錄上,用戶通過公衆號對話獲取鏈接下載,鏈接一直有效。

這樣就會存在一個問題,個人網站帶寬有限,也不想放到 cdn 上面,得想個辦法生成一個臨時文件下載鏈接,在一段時間內有效,一段時間後過期,既服務了真實粉絲,也能節約成本,這年頭生活已經夠艱難了,不能做虧本生意,還沒那個實力。

下面記錄實現過程:

1. 使用 itsdangerous 庫的 URLSafeTimedSerializer 實現臨時 token

直接上代碼不囉唆

 1# 注意新版本 itsdangerous 是 URLSafeTimedSerializer
 2from itsdangerous import URLSafeTimedSerializer as Serializer
 3from itsdangerous import BadSignature, SignatureExpired
 4from fastapi.responses import FileResponse
 5
 6@app.get("/api/get_file/{token}")   # token 通過 path parameter 傳遞
 7async def get_file(token):
 8    s = Serializer("your secret key")  # 你的key,你應該懂的,我直接用的公衆號開發的那個token
 9    try:
10        # max_age = 60 * 10 標識token十分鐘內有效,超過就會觸發 SignatureExpired, 很關鍵!!!
11        to_user = s.loads(token, max_age=60 * 10)["to_user"]
12    except SignatureExpired:
13        logger.info("token超時,驗證失敗")
14        return {"status": "fail", "data": "expired token(token超時,驗證失敗)"} # 中英文信息都提供,用心良苦😄,真的有可能有讀者英文不好的
15    except BadSignature:
16        logger.info("無效token")
17        return {"status": "fail", "data": "bad token(無效token)"}
18    print("to_user", to_user)
19    # 前面是待下載文件路徑,後面是下週後的文件名,filename很關鍵,不然不夠友好,不信你去掉試試看
20    # 目前只提供一個文件下載,所以是寫死的
21    return FileResponse("api/downloads/InstMsiW.exe", filename="InstMsiW.exe")

注意 token 在 URL 請求參數中,一般來說 URL 中能合法使用的字符是有限的,所以才叫 URLSafeTimedSerializer,URLSafeTimedSerializer loads 時能通過 max_age 控制時效。

爲什麼能工作呢,翻源碼看看:

why max age work

不得不說,這還真是方便好用,好像註冊服務的激活郵件也是類似原理。只不過我這裏是直接返回下載鏈接,激活服務是繼續做業務相關信息處理。

 1### 此處省略 一萬行代碼 ###
 2# 意思公衆號用戶發送‘芝麻開門’,就回復一個臨時下載地址給用戶使用
 3if Content == "芝麻開門":
 4    s = Serializer("your secret key")    # 你的key,你應該懂的,我直接用的公衆號開發的那個token
 5    token = s.dumps({"to_user": toUser}) # 一般用一個唯一標誌進行dumps,dumps可以簡單理解爲加密,loads類比爲解密
 6    logger.info("開始發送下載鏈接")
 7    print("token:", token)
 8    content = f"InstMsiw臨時下載地址(請使用瀏覽器打開,10分鐘內下載鏈接有效): https://stock.mephisto.cc/api/get_file/{token}"
 9    replyMsg = reply.TextMsg(toUser, fromUser, content)
10    return replyMsg.send()

2. 各種示例

所謂有圖有真相,沒錯的話,發送‘芝麻開門’,返回不同的下載地址,鏈接十分鐘內有效,應該不會有人這麼無聊一直刷這個。

temporary download

假設超過了 10 分鐘,會提示超時,和預期一致。

download expired

刪掉 4 個字符,模擬測試錯誤 token 的情況,工作正常。

bad token

3. 總結

臨時下載鏈接的功能還挺有意思的,當然還可以加下載次數限制,演員黃渤說:“ 我看就沒有這個必要了吧! 防君子不防小人。”

上面是我結合網上的各種信息自己實踐摸索的,編程高手可能還有更加簡便的方法。

我不是高手,目前能實現出來的只能是這個樣子,能解決遇到的現實問題,也希望對讀者有所幫助。

最後修改於: Tuesday, January 30, 2024

相關文章:

翻譯: