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

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

为了保护文件,还希望用户每次向后端发送下载文件的请求时,后端能生成一个一次性的链接,用户通过该链接下载一次文件后,该链接即失效。这样即使用户把链接发送给其他用户,其他用户也无法再通过该链接下载文件。
后端是 Express.js + MongoDB 的架构,要想实现这样的需求,实现的思路应该是怎样的呢?

我觉得可以换个思路。CDN 防盗链的实效时间改成一个很短的时间(10 秒),这样在不修改逻辑的情况下最方便实现这个需求

前端先都下到内存里,然后再保存到系统文件,此时再向服务器报告下载完成。

公司没有买 CDN ,成本不固定,领导不同意……

#3 。。。就我体验来说,使用服务器带宽拉到可以下载比 oss+CDN 大部分情况下都贵很多

现在业务体量小,所以暂时不考虑,等后面成本上来了再和领导说这个事,到时候他们更容易同意。

想到一个问题,如果这个链接用户没访问过,通过微信分享给了别人,因为微信会默认访问这个链接,会不会导致这个链接失效?

链接是在用户进行“点击下载按钮”这样的操作时才生成的,而不是加载页面时就获取。

并且后端也会判断用户是否通过该链接下载了文件,没有下载的话链接也不会失效。

你还得必须判断下载流是否读取完成, 不然下到一半网络出问题, 断点续传就失效了

对于用户要下载的文件可以临时生成一个 uid 和对应的文件路径存储在数据库中,链接带上 uid 就可以了。后端通过链接的请求去查找数据库有没有对应的 uid ,有的话就取对应的路径的文件发送回去,下载完事后删除 uid ,没的话就显示对应的无文件就行了。大概逻辑就这样吧,不一定非得查 uid 的有无,还可以多加字段控制 uid 是否有效,是否过期,记录链接的生成及下载时间等

文件体积小,普遍都不到 1M ,这个问题放到后面再考虑,不过依然感谢~

一次性链接的话存内存中都没啥问题

伪需求,用户能把下载链接发给其他人,也就能把下载的文件发给其他人
基本上限制链接的有效时间就可以解决了
一次性链接的需求主要是某度网盘这种需要根据下载人区别限速,以及带广告的网盘的防盗链

我这边也只能做网页这个层面能够控制的操作,用户拿到文件后会怎么做我们这边就不控制了。

用 jsontoken 储存唯一标识(再加一个随机字段

用户下载后将唯一标识变更

我们以前做过类似的:

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

如果想做成断点续传、以及大的文件,还可以序列化 Blob 后,存到 IndexedDB ,前后端需要协调传了哪几个片段。

启动一个 minio 服务不就好了

你这目的无非防止人直接薅羊毛。1.下载必须登陆; 2.下载链接加个有效期,过期后重新获取。

不应该生成一次性链接,而是应该通过前端调用接口下载到内存,然后再吐给用户。链接一次性,用户可以生多次吧?所以你的下载地址不能只通过 URL 判断,还得配合 Headers 或者 Body 中的身份信息。

链接一直都在,只是维护一个 TTL ,超时了请求返回 404 ,否则 sendFile

url “/download/tmp/:uuid” 通过 redis 维护每个 uuid 的有效性,key 过期了就 404

developer.mozilla.org/zh-CN/docs/Web/API/Blob

去掉下载链接,点击按钮后用 ajax 发起请求,然后把返回数据转成 Blob ,通过浏览器保存。

安卓微信有个问题,它只能调用其他 APP 来下载文件,安卓微信内的网页是没法下载文件的。就是因为这个问题,才不得不生成一个链接。