V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
Kinnikuman
V2EX  ›  程序员

请教下后端大佬,关于文件上传逻辑的实现

  •  
  •   Kinnikuman · 1 天前 · 4155 次点击

    小弟只写过前端,没写过后端,现在写个小项目需要后端的一些实现。有个逻辑不太清楚应该怎么实现。

    文件上传使用 minio 做文件存储。当前端想上传文件时候,前端请求一个 api 给后端,后端生成一个预签名的 minio url ,前端拿到这个上传链接进行上传。

    上传完成后,前端是否需要再调用一次接口,将上传完成这个动作通知后端。

    虽然 minio 有 webhook 功能,支持上传完成后调用后端接口通知后端程序。但这样和前端是并行的。上传完成后需要将一些信息写入到数据库,比如数据库有一个 state 状态标志着是否上传完成,以及上传完成后的 minio 直链。

    如果使用 webhook 这种,前端上传完成后会立即刷新页面,但此时后台还没有完成数据库更新操作,数据刷新会不及时。

    但是如果使用再加一个 api 让前端调用,感觉有点复杂,一个上传逻辑需要 3 个接口请求。

    所以最佳实践是什么样的?

    43 条回复    2025-01-17 14:54:23 +08:00
    realpg
        1
    realpg  
       1 天前
    肯定要通知啊。工作嫌麻烦,不如回家躺平
    Kinnikuman
        2
    Kinnikuman  
    OP
       1 天前   ❤️ 2
    @realpg 我现在就是在家躺平呢,没事干。写一下代码打发时间。
    seers
        3
    seers  
       1 天前 via iPhone
    用 mq 解耦啊
    xfurther01
        4
    xfurther01  
       1 天前   ❤️ 2
    具体流程如下:
    1 、前端调用后端文件上传认证接口:CreateFile ,获取 s3 地址和临时认证信息。
    2 、前端根据 CreateFile 接口获取的 s3 信息,使用 s3 sdk 上传文件。例如( https://aws.amazon.com/cn/developer/)
    3 、前端调用后端文件上传完成通知接口:UpdateFile ,通知后端上传结果和文件名。
    4 、前端调用后端 GetFile 获取文件下载地址。
    shadowyue
        5
    shadowyue  
       1 天前   ❤️ 2
    前端通知你和不通知你的情况你都要处理。
    万一前端上传完文件刚好流量用完了呢
    a949690645
        6
    a949690645  
       1 天前
    我认为不需要通知,数据库里不存关于文件的任何信息。
    yyttrr
        7
    yyttrr  
       1 天前
    搜一下阿里云 oss sts 上传
    IvanLi127
        8
    IvanLi127  
       1 天前
    一般文件不是至少关联一个实体实例吗?文件传完前端不用让后端更新对应的实体字段?前面预签名是要临时权限,把业务逻辑分一部分写在那里面才叫麻烦...
    xiaoming1992
        9
    xiaoming1992  
       1 天前 via Android   ❤️ 1
    如果是自己的玩具项目,就没必要通知了,后端把上传密钥和 pathname 返回给前端,并直接把这个 pathname 存到数据库中,直接当成前端已上传成功(万一部分极端情况前端上传失败,就自己走编辑流程,重新从头上传一次呗)
    neutrino
        10
    neutrino  
       1 天前 via Android
    建议后端调用文件储存接口,并处理回调。这样前端只要把文件扔给后端并等待 URL 返回就好
    mumbler
        11
    mumbler  
       1 天前
    用 cursor ,把你需求说出来就行了
    Kinnikuman
        12
    Kinnikuman  
    OP
       1 天前
    @xiaoming1992 这个方法确实方便。直接把桶的策略改成所有人都可以 read

    Access permission for `minio/bucket` is `download`
    qwertyzzz
        13
    qwertyzzz  
       1 天前
    @Kinnikuman 哈哈哈
    spritecn
        14
    spritecn  
       1 天前   ❤️ 1
    @a949690645 不通知会有一个问题,有些文件传了根本没用过,这些文件积压下来,时间长了有几百 G,根本就找不到
    spritecn
        15
    spritecn  
       1 天前
    @realpg REPS
    SingeeKing
        16
    SingeeKing  
       1 天前
    歪个楼,要怎么解决恶意用户调用上传接口上传了一堆文件不通知后端的;加个定时任务来检查 s3 里面有没有不该存在的文件吗
    guanzhangzhang
        17
    guanzhangzhang  
       1 天前
    首先是权限问题,oss 有 sts ,就相当于临时身份
    前端拿 sts 直接上传 oss ,分段上传后调用 complete 请求给后端
    Nitromethane
        18
    Nitromethane  
       1 天前   ❤️ 1
    @SingeeKing 有个思路是
    用于上传的临时 bucket 设置一个过期时间,比如 1 小时,并限制单个文件大小。
    调用 update 上传成功后的文件,拷贝到真正要归档的 bucket 中。
    Oceanhime
        19
    Oceanhime  
       1 天前
    桶读权限开放的话,小心有人恶意 list 然后下载数据或者刷流量...

    我接手过的项目都是把文件上传+回调的所有逻辑统一交给后端处理,前端只调用 /upload 然后显示状态即可。对于公开的项目,你不能确保前端一定不会出错,如果 callback 执行中断或者恶意被执行,往小了说可能会影响数据完整性,如果这个 callback 涉及到敏感逻辑,可能出现安全问题。
    sagaxu
        20
    sagaxu  
       1 天前   ❤️ 2
    不如把问题泛化一下,三方调用,客户端,后端,第三方,

    1. 客户端请求后端,后端调用第三方生成凭据后返回给客户端使用。
    2. 客户端拿着凭据调用第三方,完成后应当如何?

    有两个通知途径,

    1. 客户端通知服务端调用结果
    2. 第三方回调通知服务端

    第 1 种方式,实时性比较高,用户体验更好,但很容易因为客户端网络或者关闭页面,导致通知丢失。
    第 2 种方式,实时性稍差,但可靠性很高,可以按照一定时间间隔多尝试几次通知。
    第 3 种方式,属于 fallback 兜底,服务端周期性轮询未知状态的凭据,做相应处理。

    第 2 种是必须的,实时性要求高,再加上第 1 种。


    更常见的场景是微信支付宝的支付。
    ugpu
        21
    ugpu  
       1 天前
    @SingeeKing
    1.发起请求我要上传文件
    1.1 验证前端的 token 文件长度 文件名称 等文件 文件归属用户 信息
    1.2 服务器接收到 验证完成预生成一个文件
    1.3 客户端开始上传服务器开始写入
    1.4 客户端上传完成把预生成文件写入一个标记为完成状态
    1.5 客户端拉取列表展示最新的文件列表

    你说的这个在第一步就已经完成了.
    ugpu
        22
    ugpu  
       1 天前
    @sagaxu
    看到你这个 早期的美团外卖就做过这种”蠢事“
    就是大规模应用场景几乎不能用客户端通知
    美团外卖当时的是: 付款跳回 app 本身才通知支付完成->后台->商家有新的订单.
    现在美团估计是双端都会通知. 做了锁处理接着执行后续逻辑.
    npe
        23
    npe  
       1 天前
    当然要,如果嫌麻烦,可以写一个 lambda ,由 S3 存储桶触发后端来更新数据库状态。
    xqiang
        24
    xqiang  
       1 天前
    @neutrino +1 ,上传文件难道不是应该后端来处理吗
    vikaptain
        25
    vikaptain  
       1 天前
    @xqiang 这样会占用后端服务器的带宽。
    我现在的操作是不标记是否完成,前端上传之前先获取上传地址,上传完成之后把文件地址给后端,后端保存文件地址。
    CyouYamato
        26
    CyouYamato  
       1 天前
    让后端管,前端不要老是想着这个那个.老老实实调样式和调接口就好了.
    xiaogu2014
        27
    xiaogu2014  
       1 天前
    首先。建议使用 webhook 。通知数据库改状态。不应该依赖前端告知。

    遇到的问题是```前端上传完成后会立即刷新页面,但此时后台还没有完成数据库更新操作,数据刷新会不及时```
    前端上传完成。这个文件的状态还是`处理中`。 只有等到 db 状态改了才会变成`完成`。

    前端上传完成 != 文件全部可用。
    zhuweiyou
        28
    zhuweiyou  
       1 天前
    两个步骤
    1.上传文件 2.提交上传得到的 url 和其它表单字段

    上传做成业务无关的操作, 没有所谓的通知
    Habyss
        29
    Habyss  
       1 天前
    最常用做法是: 直接把文件丢给后端.

    如果已经到考虑宽带问题了, 就不是常用做法了. 而且你是小项目, 应该还没到考虑后端服务器宽带问题的地步吧.
    FaiChou
        30
    FaiChou  
       1 天前 via iPhone
    @mumbler 是的 我就是用 cursor ,但我需要先了解下,说不定有更好的方案。
    yooomu
        32
    yooomu  
       1 天前
    作为一个后端,个人经验是直接传给后端,让后端去存到对象储存。不要在前端放太多逻辑,幺蛾子多
    dilu
        33
    dilu  
       1 天前
    没明白为什么搞的这么复杂,一般来说用对象存储的话,都是前端请求后端获取一个短期有效的 token ,然后用这个 token 直接上传到对象存储,从而获得资源 url ,最后看用户是否提交而把 url 一起提交给后端

    当然 minio 有没有临时 token 我不太清楚,建议翻一下官方文档
    vincentWdp
        34
    vincentWdp  
       1 天前
    @xfurther01 我可以提供一个思路:
    既然是 s3, 那可以提供 临时上传 token 给前端, 这个 token 给一个很小的权限, 比如你可以给每个用户提供一个 object key 前缀, 用户只能把文件上传到这个前缀下. 因此 token 也是限制到这个前缀, 这样避免用户乱传. 而且文件名尽量随机, 不然用户可能会对其他用户的文件做尝试.
    第二就是, 后端提供文件地址拼接方法, 前端用 s3 sdk 上传成功后, 按照拼接方法把文件 url 拼接出来, 再传给后端. 前端胡乱拼接的数据不会污染其他用户的数据.
    keller
        35
    keller  
       1 天前
    文件应该是上传给后端,由后端来决定存储方式(本地、oss 、s3 、minio )
    0xD800
        36
    0xD800  
       1 天前 via Android
    通知,定时清理没通知的文件。
    另外通知的时候要带上业务 id 用于检验文件是否真实用到了。
    没用到的定时清理。
    不然不就随便白嫖
    neoblackcap
        37
    neoblackcap  
       1 天前   ❤️ 2
    @xqiang 现在上传文件都是直接上传到 OSS 服务,后端只做元数据处理(写数据库记录)。具体的文件上传功能都是前端做的。
    这样有一个极大的优点就是,文件上传并不占用服务器带宽。否则文件上传到国内的服务器,一个人就可以把那可怜的公网服务器带宽打满了。
    yh7gdiaYW
        38
    yh7gdiaYW  
       23 小时 14 分钟前
    @SingeeKing 我们是后端在文件表建一条记录,返回预签名上传链接,上传完成并且前端提交表单成功后,把文件表的状态改掉,定时扫状态不对的文件清理
    yh7gdiaYW
        39
    yh7gdiaYW  
       23 小时 12 分钟前
    @dilu 表单里有个上传文件的地方,有人一直在这上传但又不提交表单,不加上传完成的确认这些垃圾文件如何处理? 18 楼的临时桶是一种方案,但其实也挺麻烦的且对大文件不友好
    dilu
        40
    dilu  
       23 小时 8 分钟前
    @yh7gdiaYW 我们之前是先传到私有桶里面,用户自己看的话是带签名的,如果用户不提交,链接就永远是私有链接,分享出去也看不到。提交后在后端检查 url 是不是私有桶的,然后重新传到公有桶再替换 url

    如果你不想搞这一套,可以用私有桶的短时间签名的方式,格式化存储图片等资源,需要用的时候,去对象存书获取一个临时的 sign ,有效期几分钟而已,这样即使对方获取了 url 转发出去也很快就失效了。
    yh7gdiaYW
        41
    yh7gdiaYW  
       22 小时 59 分钟前
    @dilu 你们这个私有桶看着跟楼上的临时桶用途是类似的,小文件这么做没问题,对传数 GB 甚至更大文件需求的话就不合适了,对象存储复制大文件的速度一般
    yh7gdiaYW
        42
    yh7gdiaYW  
       22 小时 58 分钟前
    @dilu 第二种不用私有桶的方式,没有解决反复编辑上传产生垃圾文件的问题
    sazima
        43
    sazima  
       19 小时 14 分钟前
    用 formdata 一个接口就行。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2814 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 02:09 · PVG 10:09 · LAX 18:09 · JFK 21:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.