抖音项目笔记——文件上传 tips | 青训营笔记

285 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记。

一、背景

笔者所在小组抖音项目采用的是 OSS 存储视频的方案。虽然各家服务提供厂商的接口等存在细微差别,但许多处理办法还是有共通之处,如文件校验(是否是视频文件)、文件重命名等。故写文以记录之。 (才不是因为写不够 5 篇笔记不给结业证书23333)

二、通用解决思路

2.1 文件校验

  • 整体思路 因用户不受信任,不仅会上传视频文件,还可能上传其他文件恶意破坏用户体验甚至危及用户系统安全。笔者通过最简单的方式,即判断后缀名来校验用户上传文件是否为视频文件。

  • 代码展示

// 允许上传的文件后缀
var writeSuffixList = map[string]struct{}{
   "mp4":  {},
   "avi":  {},
   "wma":  {},
   "mpeg": {},
   "mpg":  {},
   "mov":  {},
}

...
// 获取后缀名
lastDotIndex := strings.LastIndex(fileNameOrg, ".")
if lastDotIndex < 0 {
   return "", "", errors.New("miss suffix")
}
suffix := fileNameOrg[lastDotIndex+1:]
suffix = strings.ToLower(suffix)
// 查看是否在白名单里
if _, ok := writeSuffixList[suffix]; !ok {
   return "", "", errors.New("suffix is invalid")
}
...
  • 特点 使用 Map 实现的白名单,将 Value 部分用空结构体代替布尔值达到集合的效果。
  • 空结构体没有长度,不携带任何信息,只强调 Map 的键是可用的,可以节约空间。
  • 在判断后缀是否在白名单时,只用 if语句 配合 逗号ok语句 即可实现,代码写起来简单。若采用数组保存白名单方式,需要循环遍历。
  • 【注意】虽然空结构体能这么用,但因节约的内存不多且语法复杂不推荐使用。(写代码的时候只记得《GPL》中说了这种用法,忘了不推荐使用这句话了,写博客的时候翻书才看见,所以只能说特点,不能说亮点了)

2.2 文件重命名

  • 整体思路 因用户上传的文件名可能有恶意代码,故包含文件上传的服务均需对文件名进行重命名。笔者的命名策略是 前缀+hash(文件名+时间戳)+后缀名
    前缀为:video/YY/MM/DD
    哈希算法为:md5 (原谅笔者之前没听说过雪花算法)

  • 代码展示

m := md5.New()
if _, err := io.WriteString(m, fileNameOrg[:lastDotIndex]+time.Now().String()); err != nil {
   return "", "", err
}
fileName := fmt.Sprintf("%x", m.Sum(nil))
pre := time.Now().Format("06/01/02")
  • 特点 数据库存储的视频链接字段为 play_url,该字段不存储视频的全链接,只存储上述代码生成的 前缀+文件名+后缀。与 OSS 绑定的域名存储在常量中,在需要视频链接时,将域名常量和数据库中链接后缀做拼接返回即可。这样做一是有利于节约数据存储空间;二是解耦,与 OSS 绑定的域名更换后只需要变更常量的值即可,不需要批量修改数据库中的值。

三、改进的一点思考

本来以为考虑的还算完整,直至答辩中午看到了评分细则,看到是否考虑重复视频上传。这点确实没考虑,但是瞬间想到了之前看的一篇讲百度网盘存储策略的文章,简单讲一下重复视频的处理思路。

讲之前要明白为什么要考虑重复视频上传,一个字——”钱“。将视频之类的大文件存储到 OSS 是需要大量空间的,而重复视频上传会白白占用大量存储空间,这样会产生大量服务费,能处理这个问题就能省钱。

简单的思路就是在上传每个视频时,计算每个文件内容的哈希,通过查表比对是否有同样哈希值的记录存在,若存在则不上传该文件,直接在数据库中记录之前相同文件的链接即可。若不存在再上传之,并记录其链接和哈希值。

所以别看百度网盘每个人空间那么大,但因为重复文件的存在,每个人其实并没用使用到它显示的那么多。

四、参考资料


看到这儿了都,可以求个 star 吗,23333: github.com/ByteDanceCa…