一、背景
笔者所在小组抖音项目采用的是 OSS 存储视频的方案。虽然各家服务提供厂商的接口等存在细微差别,但许多处理办法还是有共通之处,如文件校验(是否是视频文件)、文件重命名等。故写文以记录之。 (才不是因为写不够 5 篇笔记不给结业证书23333)
二、通用解决思路
2.1 文件校验
-
整体思路 因用户不受信任,不仅会上传视频文件,还可能上传其他文件恶意破坏用户体验甚至危及用户系统安全。笔者通过最简单的方式,即判断后缀名来校验用户上传文件是否为视频文件。
-
代码展示 ```go // 允许上传的文件后缀 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 ~~(原谅笔者之前没听说过雪花算法)~~
- 代码展示
```go
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 是需要大量空间的,而重复视频上传会白白占用大量存储空间,这样会产生大量服务费,能处理这个问题就能省钱。
简单的思路就是在上传每个视频时,计算每个文件内容的哈希,通过查表比对是否有同样哈希值的记录存在,若存在则不上传该文件,直接在数据库中记录之前相同文件的链接即可。若不存在再上传之,并记录其链接和哈希值。
所以别看百度网盘每个人空间那么大,但因为重复文件的存在,每个人其实并没用使用到它显示的那么多。
四、参考资料
- 《Go 程序设计语言》
-
[在Gin中上传视频到七牛云 青训营笔记](https://juejin.cn/post/7095634184328347662) - 讲百度网盘策略那个文章,找不到链接了
看到这儿了都,可以求个 star 吗,23333: https://github.com/ByteDanceCamp/micro_tiktok