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
欢迎关注微信公众号,留言交流。

相关文章:

翻译: