Express.js 生成一次性下载链接

2022-11-08 15:09:29 +08:00
 dream4ever

书接上回:如何实现付费下载功能?

现在已经实现的付费下载功能如下:

  1. 用户在微信中支付成功后,保存微信所返回的订单信息和所购买文件的 id ,同时订单信息中还包含用户微信号的 openid ,用来唯一标识用户。
  2. 用户在前端发起下载请求时,后端 Express.js 查询订单表中是否有该用户的 openid 和和所下载文件的 id ,有的话就用 res.sendFile() 把文件发送给前端。

为了保护文件,还希望用户每次向后端发送下载文件的请求时,后端能生成一个一次性的链接,用户通过该链接下载一次文件后,该链接即失效。这样即使用户把链接发送给其他用户,其他用户也无法再通过该链接下载文件。

后端是 Express.js + MongoDB 的架构,要想实现这样的需求,实现的思路应该是怎样的呢?

2300 次点击
所在节点    程序员
22 条回复
pota
2022-11-08 15:12:33 +08:00
我觉得可以换个思路。CDN 防盗链的实效时间改成一个很短的时间(10 秒),这样在不修改逻辑的情况下最方便实现这个需求
fgwmlhdkkkw
2022-11-08 15:15:23 +08:00
前端先都下到内存里,然后再保存到系统文件,此时再向服务器报告下载完成。
dream4ever
2022-11-08 15:16:51 +08:00
@pota 公司没有买 CDN ,成本不固定,领导不同意……
pota
2022-11-08 15:18:03 +08:00
@dream4ever #3 。。。就我体验来说,使用服务器带宽拉到可以下载比 oss+CDN 大部分情况下都贵很多
dream4ever
2022-11-08 15:19:20 +08:00
@pota 现在业务体量小,所以暂时不考虑,等后面成本上来了再和领导说这个事,到时候他们更容易同意。
LinsVert
2022-11-08 15:20:17 +08:00
想到一个问题,如果这个链接用户没访问过,通过微信分享给了别人,因为微信会默认访问这个链接,会不会导致这个链接失效?
dream4ever
2022-11-08 15:23:31 +08:00
@LinsVert 链接是在用户进行“点击下载按钮”这样的操作时才生成的,而不是加载页面时就获取。

并且后端也会判断用户是否通过该链接下载了文件,没有下载的话链接也不会失效。
w88975
2022-11-08 15:31:08 +08:00
你还得必须判断下载流是否读取完成, 不然下到一半网络出问题, 断点续传就失效了
lalalaqwer
2022-11-08 15:32:03 +08:00
对于用户要下载的文件可以临时生成一个 uid 和对应的文件路径存储在数据库中,链接带上 uid 就可以了。后端通过链接的请求去查找数据库有没有对应的 uid ,有的话就取对应的路径的文件发送回去,下载完事后删除 uid ,没的话就显示对应的无文件就行了。大概逻辑就这样吧,不一定非得查 uid 的有无,还可以多加字段控制 uid 是否有效,是否过期,记录链接的生成及下载时间等
dream4ever
2022-11-08 15:44:15 +08:00
@w88975 文件体积小,普遍都不到 1M ,这个问题放到后面再考虑,不过依然感谢~
ysc3839
2022-11-08 16:01:00 +08:00
一次性链接的话存内存中都没啥问题
mercury233
2022-11-08 16:16:42 +08:00
伪需求,用户能把下载链接发给其他人,也就能把下载的文件发给其他人
基本上限制链接的有效时间就可以解决了
一次性链接的需求主要是某度网盘这种需要根据下载人区别限速,以及带广告的网盘的防盗链
dream4ever
2022-11-08 16:23:53 +08:00
@mercury233 我这边也只能做网页这个层面能够控制的操作,用户拿到文件后会怎么做我们这边就不控制了。
PunchlY
2022-11-08 16:39:59 +08:00
用 jsontoken 储存唯一标识(再加一个随机字段

用户下载后将唯一标识变更
humbass
2022-11-08 16:55:58 +08:00
我们以前做过类似的:

- 每个下载 ID 在 redis 上做一个标记;
- 用户下载的时候使用 XMLHttpRequest 接口缓存成 Blob 对象
- 当前 request 结束后,远程清除 redis 标记;

如果想做成断点续传、以及大的文件,还可以序列化 Blob 后,存到 IndexedDB ,前后端需要协调传了哪几个片段。
zy445566
2022-11-08 20:20:34 +08:00
启动一个 minio 服务不就好了
wellerman
2022-11-08 20:44:13 +08:00
你这目的无非防止人直接薅羊毛。1.下载必须登陆; 2.下载链接加个有效期,过期后重新获取。
IvanLi127
2022-11-08 22:13:09 +08:00
不应该生成一次性链接,而是应该通过前端调用接口下载到内存,然后再吐给用户。链接一次性,用户可以生多次吧?所以你的下载地址不能只通过 URL 判断,还得配合 Headers 或者 Body 中的身份信息。
gen900
2022-11-08 22:58:16 +08:00
链接一直都在,只是维护一个 TTL ,超时了请求返回 404 ,否则 sendFile
gen900
2022-11-08 23:01:09 +08:00
url “/download/tmp/:uuid” 通过 redis 维护每个 uuid 的有效性,key 过期了就 404

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/893595

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX