这是我参与「第三届青训营 -后端场」笔记创作活动的第五篇笔记。
前面几篇主要都是课程笔记,这篇记录一下项目进行过程中遇到的问题以及相应的解决方案,供自己和后面相同的项目参考。
相关链接
1 视频封面生成
问题
抖音app上传界面没有提供封面的上传,但feed流获取的视频包含预览图片的url,因此需要生成预览封面。
解决方案
使用开源的ffmpeg工具截取视频的某一帧作为封面,最佳实现应该是截取具有代表性的一帧,这里就偷懒截取第一帧了。
实现
- 下载
ffmpeg.exe程序,放在项目文件夹 - 使用go语言设置参数,并调用cmd命令运行
ffmpeg.exe来截取视频的第一帧作为视频封面。
input := path + videoName
output := path + pictureName
cmdArguments := []string{"-i", input, "-t", "1", "-r", "1", "-q:v", "2", "-f", "image2", output}
cmd := exec.Command("ffmpeg", cmdArguments...)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
//log.Fatal(err)
return "", err
}
2 GORM查询出错
问题
有个功能是判断用户是否给视频点赞,实际项目中通过用户userId和视频videoId查询点赞表记录,来获取是否点赞。当用户未登录时也可以调用这个函数,当时的实现是用户未登录时传入userId为0,这样就解决了点赞状态的获取问题。理论上未登录用户肯定是没有任何给视频点赞的记录,但实际测试中所有视频均显示点赞。
问题分析
经过一步步测试,最终发现问题出在查询语句上。
result := db.Where(&Favorite{UserId: userId, VideoId: videoId}).Limit(1).Find(&favorite)
当用户userId为0时,GORM会默认忽略这个条件,因此实际执行的语句相当于
result := db.Where(&Favorite{VideoId: videoId}).Limit(1).Find(&favorite)
解决方案
- 使用带占位符的
where语句 - 使用
map条件传入参数 - 将未登录时的用户
userId由0改为-1
3 Token加密
问题 多数服务都是通过传递token来获取,因此需要保证不同用户不同时间有不同的token且不能随意被篡改,并且token应该要做到无状态
解决方案 采用带时间戳的加密算法来生成token,传入的token仅通过解密就验证是否合法,并获取userId和expireTime来判断是否过期,而不必经过数据库查询。
实现
// aes加密并生成token
func GenerateToken(userId int, keyString string) (string, bool) {
timeStamp := time.Now().Unix()
data := []byte(fmt.Sprint(userId) + "." + fmt.Sprint(timeStamp))
key := []byte(keyString)
crypt, err := aesEncrypt(data, key)
if err != nil {
fmt.Println(err)
return "", false
}
token := base64.StdEncoding.EncodeToString(crypt)
return token, true
}
//验证和解密
func ParseToken(token string, keyString string) (string, bool) {
key := []byte(keyString)
data, err := base64.StdEncoding.DecodeString(token)
if err != nil {
fmt.Println(err)
return err.Error(), false
}
plain, err := aesDecrypt(data, key)
if err != nil {
fmt.Println(err)
return err.Error(), false
}
plainText := fmt.Sprintf("%s", plain)
return plainText, strings.Contains(plainText, ".")
}
必要时也可以直接获取token中的时间戳,考虑到每次操作要更新token的过期时间,因此将token存在数据库了,每次查询并更新过期时间。