V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dream4ever
V2EX  ›  程序员

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

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

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

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

    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   ·   我们的愿景   ·   实用小工具   ·   2826 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 14:49 · PVG 22:49 · LAX 07:49 · JFK 10:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.