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

各位同学朋友大家好,今天我将向大家分享我自己是如何实现大项目中的视频流/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 运行

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

参考文章

数据库响应前端

相关推荐
滑滑滑1 天前
后端实践-优化一个已有的 Go 程序提高其性能 | 豆包MarsCode AI刷题
青训营笔记
柠檬柠檬2 天前
Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题
青训营笔记
用户967136399652 天前
计算最小步长丨豆包MarsCodeAI刷题
青训营笔记
用户52975799354722 天前
字节跳动青训营刷题笔记2| 豆包MarsCode AI刷题
青训营笔记
clearcold3 天前
浅谈对LangChain中Model I/O的见解 | 豆包MarsCode AI刷题
青训营笔记
夭要7夜宵3 天前
【字节青训营】 Go 进阶语言:并发概述、Goroutine、Channel、协程池 | 豆包MarsCode AI刷题
青训营笔记
用户336901104443 天前
数字分组求和题解 | 豆包MarsCode AI刷题
青训营笔记
dnxb1233 天前
GO语言工程实践课后作业:实现思路、代码以及路径记录 | 豆包MarsCode AI刷题
青训营笔记
用户916357440954 天前
AI刷题-动态规划“DNA序列编辑距离” | 豆包MarsCode AI刷题
青训营笔记
热的棒打鲜橙4 天前
数字分组求偶数和 | 豆包MarsCode AI刷题
青训营笔记