Express.js 生成一次性下载链接 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
dream4ever
V2EX    程序员

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

  dream4ever 2022-11-08 15:09:29 +08:00 2656 次点击
这是一个创建于 1067 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

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

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

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

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

去掉下载链接,点击按钮后用 ajax 发起请求,然后把返回数据转成 Blob ,通过浏览器保存。
dream4ever
    22
dream4ever  
OP
   2022-11-09 11:28:46 +08:00   1
@vone 安卓微信有个问题,它只能调用其他 APP 来下载文件,安卓微信内的网页是没法下载文件的。就是因为这个问题,才不得不生成一个链接。
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1036 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 30ms UTC 18:28 PVG 02:28 LAX 11:28 JFK 14:28
Do have faith in what you're doing.
ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86