仿小红书源码多图短视频上传中的资源表设计与清理机制
仿小红书源码做到中后期,最容易被忽略的不是页面功能,而是图片、短视频、封面、草稿、审核状态这些后台数据逐渐失控。
图文发布支持多图上传,短视频发布需要视频文件和封面图,商品详情也会涉及商品主图、轮播图和详情图。发现页、关注页、附近页、个人主页、作品管理、收藏管理都会不断读取这些资源。刚开始数据少时,文件上传后能展示就算完成;内容量增加后,问题会集中出现:废弃图片越来越多,草稿文件长期占用空间,审核失败的内容资源没有清理,用户删除作品后文件仍然保留,短视频封面和视频状态不一致。
这类问题表面看是资源管理问题,实际会影响数据库结构、内容审核、定时任务、后台权限、文件表设计和多端展示稳定性。仿小红书源码如果只关注内容能不能发布,不提前处理资源治理,后期很容易出现文件堆积、状态混乱和数据难以追踪的问题。
一、图片视频不能只保存一个地址
在图文发布、多图上传、短视频发布、商品详情图这些场景中,很多实现会直接把图片地址或视频地址存到内容表里。早期这样做很快,但后期维护会变得困难。
一篇图文可能有 9 张图片,一个短视频可能包含视频地址、封面图、视频时长、文件大小、宽高比例,一个商品详情可能包含主图、轮播图、详情图。如果这些资源都用字符串拼接方式塞进主表,后续审核、排序、替换、删除、清理都会变得很麻烦。
更合理的方式是拆出资源表。内容表只保存内容本身,资源表保存资源归属、资源类型、文件地址、宽高、大小、排序、状态等信息。
content_post id user_id title summary content_type audit_status publish_status create_time content_media id biz_id biz_type media_type url cover_url width height duration file_size sort status create_timebiz_type 可以区分图文内容、短视频内容、商品图片、用户头像、主页封面等来源。media_type 可以区分图片、视频、封面、表情图等资源类型。这样后期查询个人主页作品、内容详情、商品详情、收藏内容时,都可以通过统一资源表读取文件信息。
资源表拆出来以后,还有一个好处:文件状态可以独立管理。上传成功但未发布、已发布、审核失败、用户删除、系统清理中,都可以通过状态字段追踪。
二、上传成功不代表资源真正可用
仿小红书类系统中,用户上传图片或短视频后,文件一般会先进入腾讯云对象存储。上传完成后,前端拿到资源地址,再提交图文、短视频或商品数据。
这里容易出现一个问题:文件上传成功了,但内容没有发布成功。比如用户上传了 6 张图片,写了一半退出页面;或者短视频上传完成后,内容审核没有通过;或者保存草稿后一直没有继续发布。这些文件如果不处理,就会形成废弃资源。
所以文件上传接口不能只返回 url,还需要生成资源记录,并标记为临时状态。内容正式发布成功后,再把资源状态改为已绑定。
public MediaVO uploadMedia(MultipartFile file, Long userId, String bizType) { String url = cosService.upload(file); Media media = new Media(); media.setUserId(userId); media.setBizType(bizType); media.setMediaType(resolveMediaType(file)); media.setUrl(url); media.setFileSize(file.getSize()); media.setStatus(MediaStatus.TEMP); media.setCreateTime(LocalDateTime.now()); mediaMapper.insert(media); MediaVO vo = new MediaVO(); vo.setMediaId(media.getId()); vo.setUrl(url); return vo; }前端提交正式内容时,不应该只传图片地址,而应该传 mediaId。后端根据 mediaId 校验资源是否属于当前用户,再绑定到内容或商品。
这样可以避免用户伪造他人的资源地址,也方便后期清理没有绑定业务数据的临时文件。
三、草稿保存最容易制造废弃文件
草稿保存是仿小红书源码中很容易被低估的功能。用户在编辑图文或短视频时,可能会频繁上传图片、删除图片、替换封面、修改话题标签、切换分类。每次操作都可能产生新的资源文件。
如果草稿只保存正文和图片地址,不记录资源状态,就很难判断哪些图片仍然有效,哪些图片已经被用户移除。
草稿资源可以采用“草稿 ID + 资源 ID”的方式绑定。用户每保存一次草稿,后端记录当前草稿实际使用的资源。被移除的资源不立即删除,而是标记为待清理,交给定时任务处理。
这样做可以避免用户短时间内反复编辑导致资源误删,也能避免长期无用文件占用空间。
对于图文发布,多图资源需要保存排序字段。用户拖动图片顺序后,资源表中的 sort 字段也要同步更新。对于短视频发布,视频文件和封面图可以都存入资源表,但 media_type 不同。
四、内容审核要和资源状态绑定
仿小红书源码中的内容审核不只审核文字。图文内容中的图片、短视频封面、短视频文件、评论图片、用户头像、商品图片,都可能进入审核链路。
如果只审核内容表中的文本,而不处理资源状态,就会出现文字审核通过但图片异常、视频封面异常、商品图片异常等问题。
内容审核可以拆成文本审核、图片审核、视频审核三个部分。发布时,内容先进入待审核状态,资源也进入待审核状态。所有审核完成后,再统一更新内容状态。
public void auditPost(Long postId) { Post post = postMapper.selectById(postId); List<Media> mediaList = mediaMapper.selectByBizId(postId); AuditResult textResult = auditService.auditText(post.getTitle(), post.getSummary()); AuditResult mediaResult = auditService.auditMedia(mediaList); if (textResult.isPass() && mediaResult.isPass()) { post.setAuditStatus(AuditStatus.PASS); post.setPublishStatus(PublishStatus.VISIBLE); mediaService.updateStatus(postId, MediaStatus.NORMAL); } else { post.setAuditStatus(AuditStatus.REJECT); post.setPublishStatus(PublishStatus.HIDDEN); mediaService.updateStatus(postId, MediaStatus.REJECT); } postMapper.updateById(post); }这里的重点不是审核接口本身,而是状态要统一。内容未通过审核时,发现页、关注页、附近页、内容搜索、个人主页作品列表都不能展示。资源状态也要同步更新,避免异常资源继续被读取。
如果内容需要人工复核,可以增加 REVIEW 状态。后台只展示待复核内容,不直接进入公开内容流。
五、用户删除作品不等于立刻删除文件
用户在作品管理里删除图文或短视频时,很多系统会直接删除数据库记录。但这会带来两个问题。
第一个问题是数据追踪困难。内容删除后,评论、点赞、收藏、浏览记录、通知记录可能还存在。如果内容主记录被硬删除,后续排查会很麻烦。
第二个问题是文件删除风险。用户删除作品后,文件可能还被收藏记录、审核记录、通知记录引用。如果直接删除对象存储文件,历史页面或后台记录可能出现资源缺失。
更稳妥的方式是软删除。内容表改为删除状态,资源表改为待清理状态。前端列表不再展示,后台仍可追踪数据。对象存储文件由定时任务延迟清理。
这样既能保证用户端看不到已删除作品,又能给系统留出数据回溯空间。
六、定时任务要处理三类资源垃圾
Quartz 在仿小红书源码中不只适合做订单超时关闭,也适合处理资源治理。
资源垃圾一般分为三类。
第一类是临时文件。用户上传了图片或视频,但没有绑定到图文、短视频、商品或头像。
第二类是废弃草稿文件。用户反复编辑草稿后,被移除的图片和封面不再使用。
第三类是删除内容文件。用户删除作品后,相关文件进入待清理状态。
定时任务可以每天扫描这些资源,根据状态和时间判断是否清理。
@Scheduled(cron = "0 0 3 * * ?") public void cleanInvalidMedia() { LocalDateTime expireTime = LocalDateTime.now().minusDays(7); List<Media> invalidList = mediaMapper.selectInvalidMedia(expireTime); for (Media media : invalidList) { try { cosService.delete(media.getUrl()); media.setStatus(MediaStatus.DELETED); mediaMapper.updateById(media); } catch (Exception e) { media.setCleanFailCount(media.getCleanFailCount() + 1); mediaMapper.updateById(media); } } }清理任务不能只删除对象存储文件,也要更新数据库状态。删除失败时不能无限重试,可以记录失败次数和失败原因,方便后台查看。
定时清理还要谨慎设置时间窗口。刚上传的临时文件不适合立即删除,因为用户可能还在编辑页面。通常可以设置 1 天、3 天或 7 天的延迟清理策略。
七、个人主页和作品管理要过滤资源状态
个人主页、作品管理、收藏管理、浏览记录、关注列表、粉丝列表这些页面,都需要读取内容和用户关系数据。资源状态如果没有处理好,很容易出现封面丢失、图片无法打开、视频封面异常等情况。
个人主页展示作品时,不能只查询用户发布的内容,还要过滤内容状态和资源状态。审核未通过、用户删除、资源异常的内容不应出现在公开主页。
收藏管理也需要注意。如果用户收藏的内容后来被删除或审核拦截,收藏列表中要么隐藏,要么展示不可访问状态,不能继续展示完整内容。
浏览记录同样如此。用户历史浏览过的图文或短视频,如果当前状态已不可见,浏览记录中也要根据业务规则过滤。
八、商品图片和内容图片不要混在一起处理
商品分类、商品列表、商品详情、商品搜索、购物车、立即购买、订单管理、在线支付这些模块中,商品图片也属于资源体系的一部分。
商品图片和内容图片虽然都可以存对象存储,但业务状态不同。内容图片受内容审核和发布状态影响,商品图片受商品上下架和库存状态影响。两者可以共用资源表,但必须通过 biz_type 区分。
商品详情图尤其容易被忽略。一些系统只维护商品主图,却没有维护详情图排序和状态。后期商品修改时,旧图、新图、已删除图混在一起,很容易造成详情页展示异常。
商品订单中还需要保存商品快照。订单生成时,商品标题、价格、主图等信息应写入订单明细,不能完全依赖商品表实时数据。否则商品图片后续被替换,历史订单中的商品展示也会被影响。
九、后台权限要控制资源操作边界
资源治理不只是技术问题,也涉及后台权限。内容审核人员、商品管理人员、系统管理员操作的资源范围不应完全一样。
Shiro 权限控制可以用于限制不同角色访问不同后台接口。例如内容审核人员可以查看待审核图文、短视频和相关资源,但短视频和相关不一定能修改商品图片。商品管理人员可以维护商品图片,但不一定能处理用户头像和内容封面。系统管理员可以查看资源清理记录和失败任务日志。
在前后端分离架构中,后台按钮隐藏并不安全。接口层必须判断当前账号是否具备资源查看、审核、删除、恢复等权限。否则只要接口地址暴露,就可能绕过页面限制。
十、四端展示依赖同一套资源状态
安卓 APP、苹果 APP、微信小程序、H5 网页端共用同一套后台时,资源状态必须统一。
如果 APP 端展示的是最新封面,小程序端还显示旧封面;H5 端能看到审核失败的图片,APP 端却看不到;用户在一个端删除作品,另一个端仍然显示图片卡片,这些问题通常不是前端适配造成的,而是资源状态没有统一管理。
内容详情接口、瀑布流接口、个人主页接口、商品详情接口,都应该从同一套资源表读取数据,并统一过滤状态。不同端可以根据屏幕尺寸做响应式适配,但不能各自维护图片视频状态。
瀑布流接口需要返回封面宽高,方便前端计算双列布局。短视频内容需要返回封面图和视频时长。商品详情需要返回主图、轮播图和详情图排序。只要接口字段稳定,多端展示才不容易混乱。
十一、资源治理比功能开发更容易被拖到后期
仿小红书源码中的图文发布、短视频发布、多图上传、话题标签、草稿保存、作品管理、商品详情、个人主页、内容审核,看起来是不同功能,背后其实都依赖同一套资源治理能力。
图片和视频如果没有独立资源表,后期很难追踪归属。
上传文件如果没有临时状态,草稿和废弃资源会不断堆积。
内容审核如果不绑定资源状态,异常图片和视频可能进入展示链路。
用户删除作品如果直接删文件,历史数据和后台追踪容易断裂。
四端共用后台如果不统一资源状态,APP、小程序、H5 很容易显示不一致。
在仿小红书源码中,对象存储只是文件保存位置,真正决定系统是否稳定的,是资源表、审核状态、清理任务、权限边界和多端接口之间的协作方式。图片、短视频、封面、商品图、头像、主页背景这些资源如果没有从一开始设计好状态流转,后期内容越多,清理成本和排查成本都会持续增加。
相关内容整理的说明材料湖南宠友信息技术有限公司是一家专注社区交友类产品、企业即时通信软件开发,为企业提供即时通信工具、垂直类内容圈子,自主研发的业界知名友猫产品拥有广大的企业用户群体https://chongyou.info/1/product/xhs.html
