视频流的功能实现大项目实践 | 青训营

各位同学朋友大家好,今天我将向大家分享我自己是如何实现大项目中的视频流/feed/的功能的。

1 准备工作

你需要一个准备一个mysql数据库用来存放数据,数据库如果是你的本机,那么很可能你的数据库别人访问不到需要自己设置,也可以自己租一个云服务器等等。还需要一个Go语言环境,我自己用的是VsCode,可能和用golang的人有些地方不同。

2 数据库配置

首先进入数据库:

在第一次进入数据库时,是需要设置密码的,可以参考下面两篇文章修改:

windows下mysql修改密码

linux下mysql修改密码

创建库和表,这里我使用的是SQL语句:

js 复制代码
    mysql> create database douyinproject;

进入数据库:

js 复制代码
mysql> use douyinproject;

创建表video_infos,创建时需要定义你创建的参数的数据类型,这里我定义了author_id、play_url、cover_url、favorite_count、comment_count、title六种类型,其他类型可以参照视频流接口 - 极简版抖音 (apifox.com)去添加:

js 复制代码
mysql> create table video_infos(author_id int,play_url varchar(50),cover_url varchar(50),favorite_count int,comment_count int, title varchar(50));

通常mysql的数据类型int表示整型,int64、int32都可以用它表示;varchar()表示的是mysql中的字符型括号中表示长度,与string、char对应。

之后通过在数据库中插入视频流的地址便可以通过后端访问,author_id可以递增写入,play_url和cover_url指视频ip和封面ip,这里可以自己将存储地址ip写入,例如将所有视频放入一个自建的ftp文件系统网址,不过我因为权限问题的最后没有按这样做。第二种方法是直接在网上找到视频和封面的网址,将它的地址写上(一般的短视频平台的视频都会无法加载出来,所以我推荐大家可以找一些大公司或者企业的官网上把它们的宣传片和封面地址直接写在这里)。favorite_count,comment_count直接写数字就可以,title写字。

例子(写字符型时记得要加""号):

js 复制代码
mysql> insert into video_infos values(1,"https://119v.pku.edu.cn/videos/20201016-115625.mp4","https://pe.pku.edu.cn/__local/1/EB/85/7A811B57783BF4CCD549C3192B5_EC4ACC6F_A6B06.jpg",1,"jdkfaldk");

如果想修改的话需要学习一些相关语法,可以看这篇文章SQL语法大全 或者我这篇文章小白学习Gin框架以及GORM增删查改项目实践 | 青训营 - 掘金 (juejin.cn)中的Go修改数据库内容。

3 配置后端

文章采用的是基于gin框架来写的:

3.1

首先创建go.mod文件以及main.go文件:

go.mod:

js 复制代码
    go mod init go.mod

在这个文件中放入了自己所使用包的路径,导入包可以用以下命令,比如自己需要导入gin的包,那么应该输入命令:

js 复制代码
    go get -u github.com/gin-gonic/gin

之后在go.mod中就会添加到相关包路径。

3.2 main

写一下主启动函数main:

新建main.go文件并写入:

go 复制代码
package main

import (
    "go.mod/config"    //表示在当前路径下的config文件夹中内容,主要是用来存放一些自定义的常量,例如数据库位置、密码、端口等等.
    "go.mod/mysql"     //数据库操作
    "go.mod/router"    //相关接口
    "fmt"
    "github.com/gin-gonic/gin"    //gin的包
)

func main() {
    r := gin.Default()     //创建服务
    
    if err := mysql.Init(); err != nil { //数据库启动失败的返回

		fmt.Printf("mysql启动失败,err:%v\n", err)

		return

    }

    defer mysql.Close()     //数据库关闭
    
    router.InitRouter(r)    //运行操作
}
    

3.3 Router层

router.go,这里我们只配置/feed/:

go 复制代码
package router

import (
	"go.mod/mysql"
	"go.mod/controller"
	"github.com/gin-gonic/gin"

)

func InitRouter(r *gin.Engine) {
	r.GET("/douyin/feed", VideoInfoHandler)     //注意这里的接口书写一定要和前端一样不然无法响应,尤其是/这样的符号
}

//将InitRouter()中的VideoInifoHandler函数写好,主要用于视频信息处理
func VideoInfoHandler(c *gin.Context) {
    data, _ := mysql.SelectVideoInfo()      //获取数据库中的视频数据
    
    ResponseSuccess(c,data)          //返回相应成功的操作
}

videoresponse.go,这里主要是ResponseSuccess()操作,返回给前端响应,文档中写出需要的三个参数为status_code、satus_msg、video_list:

go 复制代码
package router

import (
	"go.mod/model"
	"net/http"
	"github.com/gin-gonic/gin"
)

type ResCode int64     //自定义status_code参数

const CodeSuccess ResCode = 0

var codeMsgMap = map[ResCode]string{   //定义status_msg
	CodeSuccess: "success",
}

type ResponseData struct {                 //定义返回结构体,可以将其放入model层中
	Code      ResCode     `json:"status_code"`
	Msg       interface{} `json:"status_msg"`
	VideoInfo interface{} `json:"video_list"`
}
func (rc ResCode) Msg() string {         //status_msg返回错误时的定义
	msg := codeMsgMap[rc]      //若错误返回空
	return msg
}
func ResponseSuccess(c *gin.Context, videoList []*model.VideoInfo) {                     //返回响应主函数
	c.JSON(http.StatusOK, &ResponseData{
		Code:      CodeSuccess,
		Msg:       CodeSuccess.Msg(),
		VideoInfo: &videoList, //返回的应该为数据库中的信息,该信息videoList用mysql/feed中的SelectVideoInfo()获得
	})
}

model.VideoInfo表示结构体,在model层中定义了。

3.4 mysql层

feed.go,SelectVideoInfo()的操作:

go 复制代码
package mysql

import (
	"github.com/22722679/douyin-project/model"
	"fmt"
	"go.uber.org/zap"      //这里要重新go get一下
    "database/sql"
)

func SelectVideoInfo() (videoList []*model.VideoInfo, err error) {
	sqlStr := "select author_id, play_url, cover_url, favorite_count, comment_count, title from video_infos"     //执行SQL语句
	if err := db.Select(&videoList, sqlStr); err != nil {
		if err == sql.ErrNoRows {          //错误时输出
			zap.L().Warn("数据库中没有视频")
		}
	}
	return
}

mysql.go,执行数据库相关操作,如启动、登录、关闭数据库:

go 复制代码
package mysql

import (
	"go.mod/config"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)
var db *sqlx.DB

func Init() (err error) {
	db, err = sqlx.Connect("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.User, config.PassWord, config.IpMessage,config.DataBase))     
	if err != nil {
		zap.L().Error("连接数据库失败,错误码为:%v\n", zap.Error(err))
		return
	}
	db.SetMaxOpenConns(viper.GetInt("mysql.max_open_conns")) //设置数据库的最大打开连接数
	db.SetMaxIdleConns(viper.GetInt("mysql.max_idle_conns")) //设置连接空闲的最大时间,过期的连接可能会在重用之前惰性关闭。
	return
}

//数据库关闭返回空
func Close() {
    _ = db.Close()
}

3.5 model层

feed.go定义视频流VideoInfo结构体:

go 复制代码
    import "gorm.io/gorm"
    
    //视频流信息
    type VideoInfo struct {
        gorm.Model
        Id                     int        `json:"id" db:"author_id"`
        PlayUrl            string     `json:"play_url" db:"play_url"`
        CoverUrl          string   `json:"cover_url" db:"cover_url"`
        FavoriteCount     int  `json:"favorite_count" db:"favorite_count"`
        CommentCount   int      `json:"comment_count" db:"comment_count"`
        Title                string   `json:"title" db:"title"`
    }

json名称与前端对应,db名称与mysql数据库中的相对应。

3.6 config

config.go:

go 复制代码
const   User = "root"
const   PassWord = "*****"   //数据库的密码
const   IpMessage = "ip:3306"  //自己电脑的IP和数据库使用端口,一般为3306
const   DataBase = ""         //数据存放的数据库名称

3.7 运行

最终该程序就写好了,只要运行就可以了。

参考文章

数据库响应前端

相关推荐
CallBack7 个月前
Typora+PicGo+阿里云OSS搭建个人图床,纵享丝滑!
前端·青训营笔记
Taonce1 年前
站在Android开发者的角度认识MQTT - 源码篇
android·青训营笔记
AB_IN1 年前
打开抖音会发生什么 | 青训营
青训营笔记
monster1231 年前
结营感受(go) | 青训营
青训营笔记
翼同学1 年前
实践记录:使用Bcrypt进行密码安全性保护和验证 | 青训营
青训营笔记
hu1hu_1 年前
Git 的正确使用姿势与最佳实践(1) | 青训营
青训营笔记
星曈1 年前
详解前端框架中的设计模式 | 青训营
青训营笔记
tuxiaobei1 年前
文件上传漏洞 Upload-lab 实践(中)| 青训营
青训营笔记
yibao1 年前
高质量编程与性能调优实战 | 青训营
青训营笔记
小金先生SG1 年前
阿里云对象存储OSS使用| 青训营
青训营笔记