0%

图片上传逻辑改造

前言

服务器中目前堆积了一部分无效文件,这些无效文件来自下面的一些操作:

  1. 已删除信息对应的附件。
  2. 过期的聊天消息:单聊和群聊消息缓存时间都是 120 天,超过 120 天后,消息缓存会被移除,但是消息对应的附件还会保留。
  3. [上传文件] 和 [提交表单] 分成两个请求处理的功能,用户上传文件成功后,没有提交表单,这类文件就会成为垃圾文件。
  4. 修改头像、车图片等信息后,旧的文件仍然保留在服务器上未被移除。

除这些无效文件外,还有一部分重复的文件也会浪费服务器的存储空间,比如A给B连续发送多张相同的图片。

这些垃圾/无效文件,对 App 的功能并不会造成影响,唯一的影响就是会占用服务器的存储空间。就目前的用户数而言,这些垃圾文件其实并不会占用太大的存储空间,因此不作处理也是可以的。但我们针对文件的处理还包含了文件机审操作,每次文件上传都需要将文件提交机审。而 采用合适方案后,是可以减少提交文件机审的次数的,并且可以减少文件 I/O 的次数,这些都能够提高文件上传的效率。因此综合来看,还是有必要进行优化的。

方案选择

通过临时文件夹处理无效文件

  • 文件上传后,先保存到临时文件夹,之后提交表单时,再将其移动到指定的文件夹中,临时文件夹中的文件定期清理
  • 对于删除朋友圈信息这样的操作,将对应的文件移动到指定的文件夹,这个文件夹中的文件定期清理(注:这样操作后,原信息中保存的文件的 URL 将会失效
  • 对于过期的聊天文件,定期将存储聊天文件的文件夹中超出期限(120 天)的文件删除即可。

这是一种较为简单的方案,改动小,但仅仅只是解决了服务器中无效文件的问题,重复文件的问题并没有处理,并且对于文件上传的效率是没有提升的。

增加文件上传记录

这里先将大概的逻辑梳理出来,后面再对一些问题进行额外说明:

  • 增加一个 文件上传记录表,表中记录对应文件的 Hash 值,服务端收到上传的文件后,计算其 Hash 值,然后去已有的文件记录中找是否存在相同 Hash 值的文件上传记录。(文件 Hash 值计算方法后面再讲
  • 如果表中无记录,那么表示是第一次上传,将文件保存到服务器,如果需要机审则提交机审,需要压缩则进行压缩。之后增加一条记录,文件引用数设置为0,文件名设置为文件 hash 值,记录文件的审核结果,保存原图和缩略图路径,最后给 App 端返回对应的文件 url
  • 如果表中有记录,那么就直接根据现有记录来处理即可,无需再重复进行 [文件存储]、[生成缩略图]、[文件审核] 的操作,直接给 App 端返回对应的文件 url 即可。(注:如果文件缩略图没有生成过,或者没有进行过审核,那么相关操作还是要执行的;并且文件记录如果 isDelete = 1,那么表示被清理了,上面这三个操作也要执行 )
  • App 端提交表单后,根据 url 拿到 hash 值,找对应的文件上传记录,将 文件记录的引用计数加1;文件对应的数据被删除后,比如删除朋友圈,那么 文件引用数减1;如果用户上传文件后没有提交表单,那么 文件引用计数不会变更
  • 增加定时任务,定期清理 引用数为0,并且更新时间超过1年 的垃圾文件。

具体实施

目前使用的是 [增加文件上传记录] 的方式来对文件上传逻辑进行的改造,下面记录一下相关的问题和解决办法。

文件 Hash 计算

上面提到通过文件 Hash 值来处理重复图片的问题,具体处理是:文件上传记录表主键设置为文件 Hash 值;文件通过文件 Hash 值来命名

然而哈希算法存在一个问题,存在哈希碰撞的问题,任何哈希算法都无法避免,但不同的哈希算法碰撞概率不同,就 文件哈希 而言, MD5 算法已被验证出会出现碰撞的情况(两张不同的图片 MD5 值一样)。如果出现哈希碰撞的话,会导致 两种不同的图片共用一个链接 的情况,这在 App 内的表现是 文件展示错乱。不过这种碰撞概率极小,对于我们目前的场景来说,MD5 算法就够用了。如果想要减少这种情况的发生,可以考虑使用 SHA-256 算法,MD5 算法对应的 hash 值是 32 位字符,而 SHA-256 算法对应的 hash 值是 64位字符,字符位数越多,那么字符组合就更多,碰撞概率就更小。

另外,关于文件的 hash 值,即使原始内容只改动一个字节,哈希码也会完全不同。也就是说,图片的样式尺寸等信息发生变更后,它的 hash 值就会变更。

重复 Hash 值的概率虽然小,但是还是存在的,服务端后面可以考虑在逻辑上对这种情况进行处理。

文件记录表结构

关于文件上传记录表,有两种表结构:

  1. 单表结构。字段包括:文件哈希值、文件URL、文件存储路径、文件缩略图、文件审核状态、文件被引用次数,使用 文件哈希值 作为主键。
  2. 双表结构。如果想看 文件具体是被什么数据引用的,那么就需要在第1种设计的基础上 移除文件被引用次数,额外增加一个文件被引用记录表,专门记录文件被哪条数据引用了。

目前并没有要看 文件具体是被什么数据引用 的需求,因此目前使用的是第1种表结构,具体的表结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
thumbnail_chatcreate table t_app_file_upload_record
(
file_hash varchar(64) not null comment '文件哈希值'
primary key,
file_url varchar(200) not null comment '文件访问Url',
thumbnail_logo varchar(200) null comment '缩略图url - logo类缩略图(尺寸较小)',
thumbnail_chat varchar(200) null comment '缩略图url - 聊天、朋友圈中图片的缩略图',
file_path varchar(200) not null comment '文件在服务器中的存储路径',
referenced_count int default 0 null comment '文件当前被引用次数',
audit_status tinyint default 0 null comment '文件机审是否通过 默认0 0:未审核,1:通过,2:不通过',
reason varchar(400) null comment '审核不通过原因',
update_date datetime null comment '修改时间',
update_user varchar(36) null comment '修改者uid',
create_date datetime null comment '创建时间',
create_user varchar(36) null comment '创建者uid',
is_delete tinyint default 0 null comment '默认0,0:未删除,1:已删除',
version int default 1 null comment '新增时,默认1'
)
comment '文件上传记录表';

file_hash 使用 64 位存储,这样之后如果需要将 MD5 算法替换为 SHA-256 算法可以直接进行替换。

audit_status 包含如下几种审核状态:0->未审核,1->审核通过,2->审核不通过,3->审核失败,4->已提交异步审核

注意其中缩略图分成了两个字段来存储,分别是 thumbnail_logothumbnail_chat,因为目前服务端对图片的压缩处理有两种:

  1. **logo 类缩略图(width<=200 && heigh<=225)**。通过 POST /fileUpload/image - 普通图片上传 接口上传的图片,如果需要压缩,那么缩略图就是这种
  2. **聊天/朋友圈信息缩略图(width<=720)**。通过 POST /fileUpload/upload/{type} - 朋友圈、聊天文件的上传 接口上传的图片,如果需要压缩,那么缩略图就是这种

缩略图为何要单独使用一个字段存储

上面说过,图片被调整后,对应的 Hash 值会变更,缩略图和原图的哈希值就是不一样的,那么这里 为什么不将缩略图单独作为一条记录存储呢?

这是考虑到目前 App 的场景,原图和缩略图可以通过 [Hash 值 + 指定后缀] 的方式获取,通过原图可以拿到缩略图,通过缩略图也可以获取到原图,缩略图和原图是强耦合关系,它们是一起存在一起被删除。额外通过一条记录存储的话,会增加表格复杂度和代码复杂度。

然而这么处理的话,有一个缺陷,就是 服务器上会出现重复 Hash 值的图片。比如现在上传一张图片 p1,它的缩略图为 t1,增加了一条文件上传记录,文件 Hash 值记录的是 p1 的哈希值。之后将缩略图 t1 作为原图上传,就会出现找不到 t1 对应的文件哈希值的情况,因此就会重新存储,并新增一条上传记录。但这种情况 最多也只是会存在两张相同的图片t1 的哈希值被记录之后,之后就不会重新上传了,因此是可以接受的。

文件审核逻辑说明

audit_status 包含如下几种审核状态:0->未审核,1->审核通过,2->审核不通过,3->审核失败,4->已提交异步审核

  • 在上传文件时,如果需要审核,但文件状态是 [未审核] 或 [审核失败],那么重新提交机审;
  • 如果文件状态是 [审核不通过],那么后续逻辑无需执行,直接返回提示给 App 端;
  • 如果文件状态是 [已提交异步审核],那么在 redis 缓存队列中增加缓存,异步审核结束直接处理后续逻辑。(这种情况在下面 [音视频文件异步审核带来的问题] 中有详细说明
  • 审核不通过的文件,即使文件被移除了,文件记录 isDelete 被置为了 1,后面在接收到相同的文件时,仍然按 直接返回审核不通过的提示,不执行后续逻辑 来处理。
  • 如果提交的音视频在文件记录中存在并且已经完成了审核,那么文件上传接口会直接返回文件审核结果,App 端无需再等待异步回调。

除了上面的调整外,在对图片审核进行测试时发现会出现下面的问题:聊天文件上传较大的原图时,会出现审核失败的情况,原因是图片机审接口返回了审核失败的信息。针对这种情况,进行如下优化:提交的审核的图片,统一使用缩略图

文件审核包含不同场景带来的问题

文件记录表中记录文件的审核状态,这存在一个问题:同一张图片,在不同场景下使用时,需要提交审核的参数是不同的,比如 [群聊]、[单聊] 图片的审核使用的就是不同的审核参数

之前对图片审核进行过一次改造,目前图像审核使用的华为云自定义策略模式,所有场景的图片审核使用的都是同一策略,也就是说,目前图像审核的结果是可以在多个场景下复用的。

1715670095887f9ed6689bfc9cbc518ebf63ee4401124.png

音视频文件异步审核带来的问题

目前服务端中进行的图片机审是同步请求,提交机审后接口立刻返回审核结果;而音视频审核是异步审核,提交机审后,需要等待回调。

异步审核就代表 [提交审核] 到 [审核完成] 中间会有一个时间,如果用户在这个时间内上传了相同的音视频文件,不做处理的话会重复提交审核。最好是能够按照如下的方式进行处理:用户1上传了音视频,文件还在审核中,这时用户2上传音视频,无需重复审核,只要在用户1上传的音视频审核完毕后直接通知用户2即可。

具体处理办法:

  • 文件审核状态 auditStatus 增加一种状态:4 -> 已提交异步审核
  • 视频提交异步审核后,将 [待通知用户/带审核流程的任务] 缓存起来,缓存结构调整为 list 结构。异步审核还没结束时,别的用户又提交了相同的视频审核,那么再在缓存中添加一条即可。
  • 当音视频任务审核完毕后,给所有缓存的 [待通知用户] 推送通知,而所有 [带审核流程的任务] 继续执行后续的审核流程。

朋友圈文件审核包含审核流程带来的问题

常规的文件上传,如果文件需要审核,审核操作和上传操作是一起进行的。而朋友圈中的文件审核操作和上传操作是分开的,审核操作是在后续的审核流程中进行的。因此针对朋友圈文件的审核操作,需要单独进行处理。处理后的朋友圈文件上传审核流程如下:

  1. 上传文件,服务端新增文件上传记录
  2. App端提交表单,服务端更新引用计数
  3. 开始进行文件的审核,服务端更新文件审核状态
  4. 如果是音视频,等待音视频异步审核结束,拿到审核结果后,再次更新文件的审核状态。

后续可以进行的优化。由于朋友圈文件的审核操作与上传文件的逻辑是分开的,因此与其他场景下的文件上传逻辑相比,朋友圈的文件上传对 FileUploadRecord 的更新操作多了一次。如果性能太差,后续可以考虑按如下逻辑进行:第1步上传操作中直接进行审核,在第3步进行文件审核时,就可以无需审核直接拿到文件审核结果,并且无需再进行一次 FileUploadRecord 更新操作。但这种处理的劣势是:朋友圈信息中所有的 [图片/视频/音频] 都会触发审核,而原先是只要有一个附件审核不通过,后续其他附件就不会再进行审核操作了。

存在的问题。在上面的第2和第3步中,正常情况下都是能够拿到文件对应的上传记录的,因为第1步已经确保了上传记录的存在。但是存在一种特殊情况:第2第3步提交的文件链接,没有经过第1步处理,比如提交的文件链接是第三方的,这种情况下找不到对应的文件记录,并且文件在服务器也不存在。这种情况在使用接口测试工具进行测试时可能会出现,而正常通过 App 端操作时应该不会出现。

针对这种情况,服务端进行了补偿措施:如果朋友圈文件提交审核时,根据文件 url 没有找到对应的文件记录,那么直接将这个文件 url 提交审核。

另外,这种问题后续服务端也需要统一进行一下限制,即 **提交表单时上传的文件 url 必须是我们服务的,禁止第三方文件 url*。因为按照目前的文件上传逻辑,提交第三方文件url 是不会经过机审的,这容易出现问题。(后续可以考虑 App 端提交表单时,统一提交文件 Hash 值,由服务端去数据库找对应的文件 url,再进行后续操作*)

文件引用计数变更逻辑

关于文件引用计数变更的处理,最先考虑的是通过切面处理,因为切面处理更易维护。但是尝试之后发现这种处理不太适合目前的场景,切面更适合 新增文件(引用计数增加)、删除文件(引用计数减少) 的场景,比如 朋友圈信息新增、朋友圈信息删除。而 文件修改 场景就不太适合,因为这种场景涉及 旧文件引用计数减少,新文件引用计数增加,比如 用户头像变更、名片图片变更

因此目前按如下方式处理:在每个引用计数发生变化的位置,分别增加代码

此外,关于文件引用计数的计算,存在一些疑问:App 端存在一些场景,没有触发文件上传操作,但是最终会涉及文件的引用变更。比如添加商友、保存名片,这些操作没有进行文件上传操作,但是最后会将其他数据中的文件冗余到自己的信息中,比如加商友时会从 t_app_user 表中拿到 [商友头像] 填充到 t_app_user_friend 中,那这类文件的引用计数是否需要计算呢?

目前考虑是:如果保存有文件的数据,后续用户可以对该数据中的文件重新上传,那么就进行引用计数的变更;否则就无需进行引用计数的变更。两个例子:

  • 无需计算引用计数。B是A的商友,A修改商友头像,头像信息同步至B与A的商友关系中,用户之后是无法主动修改商友关系中的头像的,只能通过修改商友头像才会同步过来,因此这时商友关系中头像的变更是无需进行引用计数的修改的。
  • 需要计算引用计数。A有名片并设置了名片正反面图像,B保存A的网络名片,会增加一条 UserCardTemp (本地名片)记录,UserCardTemp 中的名片正反面图像来自网络名片,用户之后是可以对 UserCardTemp 中的图像进行修改的,因此这时 UserCardTemp 中的图像是需要进行文件引用计数变更的。

聊天文件引用计数的处理

目前的逻辑中,聊天消息的过期时间是 120 天,消息过期被移除后,附件还存在在服务器中。常规的文件上传逻辑包含两步:①. 文件上传(服务端新增 FileUploadRecord) ②. 提交表单(服务端增加 FileUploadRecord 的引用计数)。对于聊天中发送的文件,第②步提交表单操作是没有的,因此引用计数的计算需要单独处理,目前有如下三种方案:

  1. 文件上传后,立刻增加引用计数;聊天消息过期后,将引用计数减少。使用这个方案的话,需要额外增加一个聊天消息过期监听事件。并且还存在一个问题:如果文件上传引用计数增加后,聊天消息发送失败,那么聊天消息不会缓存到服务端,这条文件的引用计数就有问题,定时清理文件时这个文件就不会被清理掉。

    如果可以接受后面存在的这个问题,那么可以使用这种方案。

  2. 聊天消息发送后,增加引用计数;聊天消息过期后,引用计数减少。相比上一种方案,这种方案在 [服务端收到消息并成功缓存后] 才会增加引用计数,可以避免上面提到的问题。
  3. 聊天中上传的文件不记录引用计数的变更,对定时清理文件的逻辑进行调整,清理 [修改时间大于1年,且引用计数为0] 的文件。每一次文件引用计数的变更,都需要修改数据库。真实场景中,聊天文件的数量应该是比其他所有场景中文件数量之和还要多的,如果按照上面两种方式处理,即发送一个文件引用计数加1,那么数据库修改操作会非常频繁。而这种方案不需要记录聊天文件引用计数的变更,过期的文件也能正常被清理。

目前使用的是第三种方案,这种方案存在一个问题:发送聊天文件时,如果文件记录存在并且无需压缩和审核,那么服务端会直接返回这个文件,不进行其他处理。这时文件记录的 updateDate 是不会变化的,也就是说如果这个文件在 1 天后 updateDate 就满一年了,那么 1 天后它就被清理掉。因此,对文件上传记录进行了如下改造:**文件上传时,如果文件上传记录已存在,那么更新其 updateDate**。这个新增的逻辑只对聊天文件上传的场景进行了处理,因为其他场景下的文件上传都需要进行文件引用计数的变更。

这样改造后还存在一个问题:发送收藏文件转发图片 时,是不会走 上传文件 接口的,这时文件记录的 updateDate 也不会变更,这可能会导致 文件无法按照正常的逻辑被清理

  • 这两个操作中,发送收藏文件 不会影响文件的清理逻辑,因为只有聊天中只能发送未被删除的收藏文件,这表示这个收藏文件的引用计数肯定大于0,也就不会被异常清除;
  • 但是文件转发操作就会受到影响了, App 端目前处理的的文件转发逻辑是直接转发文件 url,不经过文件上传接口。看如下案例:文件 url1 天后会被清除,用户在聊天中转发这个文件,文件上传记录不会更新,1天后文件会被清除,正常的应该是这个文件在1年后才被清除。

因此需要 App 端进行如下调整:聊天中转发文件也走上传文件接口,由于服务端目前已对文件进行 Hash 去重,因此这个转发操作不会导致服务端重复存储文件,最终返回的文件 Url 与被转发的图片也是一样的。 修改为这一步是为了服务端能够更新文件上传记录的 updateDate,以便文件清理逻辑能够正常执行。

额外补充:目前文件清理逻辑是 [定期清理修改时间大于1年,且引用计数为0的文件]。由于聊天文件未接入引用计数的变更逻辑,因此后续如果需要调整文件清理的逻辑,要保证清理的文件的修改时间至少大于 120 天,也就是不能小于聊天消息缓存的过期时间

文件命名规则

上传的文件统一按照 Hash 值命名,针对图片,还需要拼接尺寸后缀以及考虑缩略图。原先的图片命名规则是根据场景划分的,不同场景下命名规则不同:

  1. POST /fileUpload/image - 普通图片上传 接口上传的图片。原图:通过 uuid 命名,不携带后缀缩略图:原图 url 后增加 _t 后缀
  2. POST /fileUpload/upload/{type} - 朋友圈、聊天文件的上传 接口上传的图片。原图:分两种情况

①. 聊天中选择原图、朋友圈图片,这种图片通过 uuid 命名,并增加 _o 后缀:http://7vw31jdn.dongtaiyuming.net/images/app/63a10c4bc01aa17f36335d2b4197ff08_o.jpg
②. 聊天中未选择原图,这种图片通过 uuid 命名,并增加 _width*height 后缀:http://7vw31jdn.dongtaiyuming.net/images/app/63a10c4bc01aa17f36335d2b4197ff08_500*450.jpg

缩略图:也分两种情况
①. 视频缩略图,视频 url 基础上增加 _t 后缀,图片是 png 格式
②. 聊天中选择原图、朋友圈图片,这种会生成缩略图,在原图 url 的基础上去除 _o 后缀。

而新的图片命名规则不再根据场景进行划分,统一按如下方式命名:

  1. 原图:通过 hash 值命名,不增加后缀:http://7vw31jdn.dongtaiyuming.net/images/app/63a10c4bc01aa17f36335d2b4197ff08.jpg?size=width*height
  2. logo 类缩略图:在原图基础上增加 _s 后缀:http://7vw31jdn.dongtaiyuming.net/images/app/63a10c4bc01aa17f36335d2b4197ff08_s.jpg?size=width*height
  3. 朋友圈、聊天缩略图:在原图基础上增加 _t 后缀:http://7vw31jdn.dongtaiyuming.net/images/app/63a10c4bc01aa17f36335d2b4197ff08_t.jpg?size=width*height

除了上面的命名规则外,原先还会在图片 url 后拼接图片尺寸信息,原先的规则如下:

  1. 视频:视频 url 后拼接缩略图尺寸,视频缩略图 url 后拼接自己的尺寸
  2. 图片:有缩略图的图片,原图后面拼接缩略图尺寸,缩略图后面拼接缩略图尺寸;
    无缩略图的图片,图片后面拼接当前图片的尺寸信息

目前统一按照下面的规则拼接尺寸:

  1. 视频:仍然保持原先的规则。
  2. 图片:原图和缩略图后面统一拼接自己的尺寸信息。

图片命名规则变更后对 App 端的影响

在图片命名规则变更后,会对 App 端现有功能造成影响。因此针对 App 端中所有涉及图片上传的功能进行了测试,测试下来发现下面几个功能受到了影响:

  1. 朋友圈图片中,如果图片被压缩了,点击查看大图时,显示的仍然是缩略图(比较模糊);点击下载图片时,会提示下载失败;收藏该图片后,收藏列表中加载不出,无法查看。
  2. [本地名片的正反面图片]、[商友/好友头像]、[车名片图片] 、[群统计/群投票图片],如果这些图片有缩略图,查看大图时显示的仍然是缩略图(比较模糊),没有展示原图。

App 端代码进行 Review,目前 App 端处理的图片加载逻辑如下:

  1. 朋友圈图片加载逻辑。在 [查看大图、下载图片、收藏图片] 操作时,会去获取原图 url:首先判断图片 url 是否携带 _o 后缀,如果携带,说明是原图,那么直接 [下载/收藏/ 展示] 即可;如果没有携带,说明是缩略图,那么加上 _o 后缀,拿到原图 url,再进行 [下载/收藏/展示]。
  2. 普通图片加载逻辑。在 [查看大图] 时,会去获取原图 url:首先判断图片 url 是否携带 _t 后缀,如果携带,说明是缩略图,那么名片详情中展示的正反面图片就去除 _t 后缀,拿到原图 url,再进行 [展示];如果未携带,说明是原图,直接展示。1715670125885b15a27dfea67b6128c2fdd9c7f3250ac.png

这两个个问题都是由于 原图缩略图命名规则发生变化 导致的问题,旧的 [获取原图 url] 逻辑还按 [朋友圈图片] 和 [普通图片] 进行了区分。服务端采用新的命名规则后,App 端针对 [获取原图 url] 逻辑,可以对所有场景的图片统一按照如下方式处理:

  • 如果图片 Url 后缀携带 _t_s 后缀,表示图片是缩略图,那么将 _t_s 后缀去除,拿到原图 Url,进行 [下载/收藏/展示];
  • 如果图片 Url 没有携带后缀,那么表示是原图,直接进行 [下载/收藏/展示] 即可。

另外,图片 url 后拼接尺寸的逻辑也发生了变更,目前测试下来调整后对 App 没有影响,但是没有对相关代码进行 review,因此可能部分被影响的场景未被测试到,如果之后发现问题再做调整。

文件存储路径优化

目前是各个模块的文件分开存放,如果同一层级的文件过多,会影响检索效率,比如聊天文件。由于现在统一使用文件 Hash 来去重,后面会出现 不同模块的数据引用同一张图片 的情况,比如聊天、朋友圈、头像使用的都是同一图片,这种情况下,分模块存储已经意义不大了,因此目前对存储路径进行改造,统一按照日期对存储路径进行命名,不再区分模块,比如在 2024-04-17 上传的文件,统一存储到 [20240417] 文件夹下面。

同一个视频 Hash 值不一样

在朋友圈和聊天中发送视频时(聊天中不勾选原图),存在一个问题,多次发送同一个视频,服务端接收到之后计算其 Hash 值,每次都不一样,Hash 值不一样,服务端就会认为它们是不同的文件,就会分别对它们执行文件保存、新增上传记录的操作。

1715670135885d74ea00fe03b38ec76217a6e8aef82ad.png

Hash 值不一样的问题上面提到过,这里的问题应该是 App 端对视频进行压缩后导致的。但是同样是 App 端进行压缩,同一张图片在 App 端多次压缩,上传到服务端时计算得到的 Hash 值却是相同的,因此猜测视频压缩后 Hash 值不一样是因为压缩算法的问题。

注:这个问题仍然需要调查,如果视频压缩后每次 Hash 值都不一样的情况的无法避免,那么就需要寻找其他的解决方案,比如通过原视频的 Hash 值来确定是否重复。

后续可以进行的一些优化

  1. 目前文件上传接口中,耗时较长的操作是获取文件的 Hash 值,该操作后续可以考虑放在 App 端处理。文件 Hash 放在 App 端做还有一个好处,App 端计算完文件 Hash 值后,直接请求服务端查询是否存在相同 Hash 值的文件,如果存在,服务端直接返回;如果不存在,再按照常规的流程进行。相比之前的流程,如果文件 Hash 存在的话,那么就能省去文件上传到服务器的操作,如果是视频,还可以额外省去压缩视频的操作。不过通过这种方式拿到的文件 Hash 值是原文件的 Hash 值,如果视频/图片需要在 App 端压缩后再发送,那么还是需要先压缩才能拿到对应文件的 Hash
  2. App 端提交表单时,统一提交文件 Hash 值,由服务端去数据库找对应的文件 url,如果未找到,则表示文件不存在,返回提示信息,这样可以避免用户通过非常规手段提交第三方 Url
  3. 二维码、群头像,这类由服务端生成的文件,也接入新的文件上传逻辑:增加对应的文件上传记录。
  4. 目前文件引用计数变更是嵌套在主逻辑之内的,与主逻辑耦合度过高,后期可以考虑针对这一块进行优化,将其从主逻辑解耦出来。
  5. 目前音视频文件在提交审核后,对应文件记录的 auditStatus 会被置为 4。而存在一种情况,阿里云音视频审核任务提交后,由于一些原因,服务端一直没有收到回调,这时音视频的审核状态将不会改变。服务端需要对这种特殊情况进行处理,可以在提交审核任务后,超过指定时间就将审核状态修改为审核失败状态。
  6. 文件记录中审核过的文件,超过一定时间后可以重新提交审核。因为审核规则和尺度可能在各个时间都是不同的,原先审核未通过的信息过1年后就能审核通过,反之亦然。

参考链接

IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?

用户上传冗余的图片文件,一般是怎么处理的呀?

对于文件服务器上的各类冗余资源,各位一般怎么处理

上传图片的逻辑,大家有什么方法的吗?

哈希碰撞与生日攻击

文件储存用 md5 做标识可行吗,要考虑会出现相同 md5 的文件的情况吗?

如何解决“上传了附件但是最后其实没用”这个问题?

产品逻辑中的图片设计方案

用户发表一篇文章,插入图片之前先上传图片到了服务器,但他并未发布就把网页关掉了,怎么处理?

请问图片存储的路径规则什么样最好?

相同文件压缩后 HASH 值(MD5)大概率不相同解决方案