一.
  1. 关于 Model

如果应用非常简单的话,我们可以在 Controller 里面处理常见的业务逻辑,但是如果
有一个功能想在多个控制器、或者多个模板里面复用的话,那么就可以把公共的功
能单独抽取出来作为一个模块(Model),Model 是逐步抽象的过程,一般会在 Model
里面封装一些公共的方法让不同 Controller 使用,也可以在 Model 中实现和数据库打交道

2.Model 里面封装公共的方法

新建 models/ tools.go

package modelsimport ("crypto/md5""fmt""time"
)//时间戳转换成日期函数
func UnixToTime(timestamp int) string {t := time.Unix(int64(timestamp), 0)return t.Format("2006-01-02 15:04:05")
}//日期转换成时间戳
func DateToUnix(str string) int64 {template := "2006-01-02 15:04:05"t, err := time.ParseInLocation(template, str, time.Local)if err != nil {return 0}return t.Unix()
}//获取当前时间戳
func GetUnix() int64 {return time.Now().Unix()
}//获取当前日期
func GetDate() string  {template := "2006-01-02 15:04:05"return time.Now().Format(template)
}//获取年月日
func GetDay() string {template := "20060102"return time.Now().Format(template)
}func Md5(str string) string {data := []byte(str)return fmt.Sprintf("%x\n", md5.Sum(data))
}
func Hello(in string) (out string) {out = in + "world"return
}

3.控制器中调用 Model

package controllers
import ("gindemo/models"
)day := models.GetDay()

4.调用 Model 注册全局模板函数

(1).main.go中

package mainimport ("fmt""gindemo/models""github.com/gin-gonic/gin""html/template"
)//main中的公共方法
func Println(str1 string, str2 string) string {return str1 + str2
}func main() {//初始化路由,会设置默认中间件:engine.Use(Logger(), Recovery()),可以使用gin.New()来设置路由r := gin.Default()//自定义模板函数,必须在r.LoadHTMLGlob前面r.SetFuncMap(template.FuncMap{"UnixToTime": models.UnixToTime, //注册模板函数"Println":    Println,})//加载templates中所有模板文件, 使用不同目录下名称相同的模板,注意:一定要放在配置路由之前才得行r.LoadHTMLGlob("templates/**/*")//配置静态web目录 第一个参数表示路由,第二个参数表示映射的目录r.Static("/static", "./static")...
}

(2).控制器,模板中:方式一

控制器UserController

func (c UserController) Add(c *gin.Context) {c.HTML(http.StatusOK, "admin/user/add.html", gin.H{ "now": models.GetUnix(), })
}

模板admin/user/add.html

<h2>{{.now | unixToDate}}</h2>

(3).控制器,模板中:方式二

控制器defaultController.go

package ityingimport (_ "fmt""gindemo/models""github.com/gin-gonic/gin""net/http"
)type DefaultController struct {}func (con DefaultController) Index(c *gin.Context) {fmt.Println(models.GetDay())c.HTML(http.StatusOK, "default/index.html",gin.H{"msg":"我是一个msg","t": 1629788010,})
}

模板default/index.html

<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "default/index.html" }}<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="/static/css/base.css"><title>首页</title></head><body>{{ template "public/page_header.html" .}}<h2>{{.msg}}</h2><h3>{{UnixToTime .t}}</h3>{{ template "public/page_footer.html" .}}</body></html>
{{end}}

5.Golang Md5 加密

打开 golang 包对应的网站:https://pkg.go.dev/,搜索 md5

方法一

package modelsimport ("crypto/md5"
)func Md5(str string) string {data := []byte(str)return fmt.Sprintf("%x\n", md5.Sum(data))
}

方法二

package modelsimport ("crypto/md5"
)func Md5(str string) string {h := md5.New()io.WriteString(h, str)return fmt.Sprintf("%x\n", h.Sum(nil))
}
二.
注意:需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"

1.单文件上传

文档:https://gin-gonic.com/zh-cn/docs/examples/upload-file/single-file/

官方案例:

func main() {router := gin.Default()// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)router.MaxMultipartMemory = 8 << 20  // 8 MiBrouter.POST("/upload", func(c *gin.Context) {// 单文件file, _ := c.FormFile("file")log.Println(file.Filename)dst := "./" + file.Filename// 上传文件至指定的完整文件路径c.SaveUploadedFile(file, dst)c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))})router.Run(":8080")
}

项目中实现文件上传

(1).定义模板 需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"

<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "admin/user/add.html" }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{.title}}</title>
</head>
<body>
<h2>演示文件上传</h2>
<form action="/admin/user/doUpload" method="post" enctype="multipart/form-data">用户名:<input type="text" name="username" placeholder="用户名"><br/><br/>头像: <input type="file" name="face"><br/><br/><input type="submit" value="提交">
</form>
</body>
</html>
{{ end }}

(2).定义业务逻辑

package adminimport ("gindemo/models""github.com/gin-gonic/gin""net/http""os""path""strconv"
)//添加:单文件上传
func (con UserController) Add(c *gin.Context) {c.HTML(http.StatusOK, "admin/user/add.html", gin.H{"title": "添加用户",})
}//单文件上传
func (con UserController) DoUpload(c *gin.Context) {//获取表单中提交的usernameusername := c.PostForm("username")//获取文件file, err := c.FormFile("face")//判断上传文件上否存在if err != nil {//说明上传文件不存在c.JSON(http.StatusInternalServerError, gin.H{"success": "fail","message": err.Error(),})return}//设置需要上传的文件目录 file.Filename 获取文件名; "./static/upload" 是基于main.go文件路由的dst := path.Join("./static/upload", file.Filename)//上传文件到指定目录c.SaveUploadedFile(file, dst)c.JSON(http.StatusOK, gin.H{"success": "true","username": username,"dst": dst,})
}

(3).增加路由配置

package routersimport ("gindemo/controllers/admin""gindemo/middlewares""github.com/gin-gonic/gin"
)//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {//路由分组: 配置全局中间件:middlewares.InitMiddlewareadminRouters := r.Group("/admin", middlewares.InitMiddleware){//单文件上传adminRouters.POST("/user/doUpload", admin.UserController{}.DoUpload)      }
}

2.多文件上传-不同名字的多个文件

(1).定义模板 需要在上传文件的 form 表单上面需要加入enctype="multipart/form-data"

<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "admin/user/addnotfilenameupload.html" }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{.title}}</title>
</head>
<body>
<h2>演示不同文件名的文件上传</h2>
<form action="/admin/user/doNotFileNameUploads" method="post" enctype="multipart/form-data">用户名:<input type="text" name="username" placeholder="用户名"><br/><br/>头像1: <input type="file" name="face1"><br/><br/>头像2: <input type="file" name="face2"><br/><br/><input type="submit" value="提交">
</form>
</body>
</html>
{{ end }}

(2).定义业务逻辑

package routersimport ("gindemo/controllers/admin""gindemo/middlewares""github.com/gin-gonic/gin"
)//添加:不同名字的多文件上传
func (con UserController) AddNotFileNameUpload(c *gin.Context) {c.HTML(http.StatusOK, "admin/user/addnotfilenameupload.html", gin.H{"title": "添加不同名字的多文件上传",})
}//不同文件名的多文件上传
func (con UserController) DoNotFileNameUploads(c *gin.Context) {//获取表单中提交的usernameusername := c.PostForm("username")//获取文件file1, err1 := c.FormFile("face1")file2, err2 := c.FormFile("face2")//判断上传文件上否存在if err1 != nil {//说明上传文件不存在c.JSON(http.StatusInternalServerError, gin.H{"success": "fail","message": err1.Error(),})return}//判断上传文件上否存在if err2 != nil {//说明上传文件不存在c.JSON(http.StatusInternalServerError, gin.H{"success": "fail","message": err2.Error(),})return}//设置需要上传的文件目录 file.Filename 获取文件名; "./static/upload" 是基于main.go文件路由的dst1 := path.Join("./static/upload", file1.Filename)//上传文件到指定目录c.SaveUploadedFile(file1, dst1)//设置需要上传的文件目录 file.Filename 获取文件名; "./static/upload" 是基于main.go文件路由的dst2 := path.Join("./static/upload", file2.Filename)//上传文件到指定目录c.SaveUploadedFile(file2, dst2)c.JSON(http.StatusOK, gin.H{"success": "true","username": username,"dst1": dst1,"dst2": dst2,})
}

(3).增加路由配置

package routersimport ("gindemo/controllers/admin""gindemo/middlewares""github.com/gin-gonic/gin"
)//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {//路由分组: 配置全局中间件:middlewares.InitMiddlewareadminRouters := r.Group("/admin", middlewares.InitMiddleware){//单文件上传adminRouters.POST("/user/doUpload", admin.UserController{}.DoUpload)  //不同名字的多文件上传adminRouters.GET("/user/addnotfilenameupload",admin.UserController{}.AddNotFileNameUpload)//不同文件名的多文件上传adminRouters.POST("/user/doNotFileNameUploads", admin.UserController{}.DoNotFileNameUploads)}
}

3.多文件上传-相同名字的多个文件

文档:https://gin-gonic.com/zh-cn/docs/examples/upload-file/multiple-file/

(1).定义模板 需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"

<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "admin/user/addcommonfilenameupload.html" }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{.title}}</title>
</head>
<body>
<h2>演示相同文件名的文件上传</h2>
<form action="/admin/user/doCommonFileNameUploads" method="post" enctype="multipart/form-data">用户名:<input type="text" name="username" placeholder="用户名"><br/><br/>头像1: <input type="file" name="face[]"><br/><br/>头像2: <input type="file" name="face[]"><br/><br/><input type="submit" value="提交">
</form>
</body>
</html>
{{ end }}

(2).定义业务逻辑

package routersimport ("gindemo/controllers/admin""gindemo/middlewares""github.com/gin-gonic/gin"
)
//添加:相同名字的多文件上传
func (con UserController) AddCommonFileNameUpload(c *gin.Context) {c.HTML(http.StatusOK, "admin/user/addcommonfilenameupload.html", gin.H{"title": "相同名字的多文件上传",})
}//相同文件名的多文件上传
func (con UserController) DoCommonFileNameUploads(c *gin.Context) {//获取表单中提交的usernameusername := c.PostForm("username")//获取formform,_ := c.MultipartForm()//获取多文件files := form.File["face[]"]//遍历文件,并上传for _, file := range files {//设置需要上传的文件目录 file.Filename 获取文件名; "./static/upload" 是基于main.go文件路由的dst := path.Join("./static/upload", file.Filename)//上传文件到指定目录c.SaveUploadedFile(file, dst)}c.JSON(http.StatusOK, gin.H{"success": "true","username": username,"message": "文件上传成功",})
}

(3).增加路由配置

package routersimport ("gindemo/controllers/admin""gindemo/middlewares""github.com/gin-gonic/gin"
)//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {//路由分组: 配置全局中间件:middlewares.InitMiddlewareadminRouters := r.Group("/admin", middlewares.InitMiddleware){//单文件上传adminRouters.POST("/user/doUpload", admin.UserController{}.DoUpload)  //不同名字的多文件上传adminRouters.GET("/user/addnotfilenameupload",admin.UserController{}.AddNotFileNameUpload)//不同文件名的多文件上传adminRouters.POST("/user/doNotFileNameUploads", admin.UserController{}.DoNotFileNameUploads)//相同名字的多文件上传adminRouters.GET("/user/addcommonfilenameupload",admin.UserController{}.AddCommonFileNameUpload)//相同文件名的多文件上传adminRouters.POST("/user/doCommonFileNameUploads", admin.UserController{}.DoCommonFileNameUploads)      }
}

4.文件上传 按照日期存储

(1).定义模板 需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"

<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "admin/user/add-by-date.html" }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{.title}}</title>
</head>
<body>
<h2>演示文件按日期上传</h2>
<form action="/admin/user/doUploadByDate" method="post" enctype="multipart/form-data">用户名:<input type="text" name="username" placeholder="用户名"><br/><br/>头像: <input type="file" name="face"><br/><br/><input type="submit" value="提交">
</form>
</body>
</html>
{{ end }}

(2).定义业务逻辑

package routersimport ("gindemo/controllers/admin""gindemo/middlewares""github.com/gin-gonic/gin"
)//添加:按日期单文件上传
func (con UserController) AddByDate(c *gin.Context) {c.HTML(http.StatusOK, "admin/user/add-by-date.html", gin.H{"title": "添加:按日期单文件上传",})
}//按日期单文件上传
/**
1.获取上传文件
2.获取后缀名,判断后缀是否正确: .jpg,.png,.gif,.jpeg
3.创建图片保存目录 ./static/upload/20230203
4.生成文件名称和文件保存目录
5.执行上传*/
func (con UserController) DoUploadByDate(c *gin.Context) {//获取表单中提交的usernameusername := c.PostForm("username")//1.获取上传文件file, err := c.FormFile("face")//判断上传文件上否存在if err != nil {//说明上传文件不存在c.JSON(http.StatusInternalServerError, gin.H{"success": "fail","message": err.Error(),})return}//2.获取后缀名,判断后缀是否正确: .jpg,.png,.gif,.jpegextName := path.Ext(file.Filename)//设置后缀mapallowExtMap := map[string]bool {".jpg" : true,".png" : true,".gif" : true,".jpeg" : true,}//判断后缀是否合法if _, ok := allowExtMap[extName]; !ok {c.JSON(http.StatusInternalServerError, gin.H{"success": "fail","message": "上传文件后缀不合法",})return}//3.创建图片保存目录 ./static/upload/20230203//获取日期day := models.GetDay()//拼接目录dir := "./static/upload/" + day//创建目录:MkdirAll 目录不存在,会一次性创建多层err = os.MkdirAll(dir, 0666)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"success": "fail","message": "创建目录失败",})return}//4.生成文件名称和文件保存目录: models.GetUnix() 获取时间戳(int64); strconv.FormatInt() 把时间戳(int64)转换成字符串filename := strconv.FormatInt(models.GetUnix(), 10) + extName//5.执行上传dst := path.Join(dir, filename)//上传文件到指定目录c.SaveUploadedFile(file, dst)c.JSON(http.StatusOK, gin.H{"success": "true","username": username,"dst": dst,})
}

(3).models/tools.go 添加获取当前时间戳函数

package modelsimport ("crypto/md5""fmt""time"
)//时间戳转换成日期函数
func UnixToTime(timestamp int) string {t := time.Unix(int64(timestamp), 0)return t.Format("2006-01-02 15:04:05")
}//日期转换成时间戳
func DateToUnix(str string) int64 {template := "2006-01-02 15:04:05"t, err := time.ParseInLocation(template, str, time.Local)if err != nil {return 0}return t.Unix()
}//获取当前时间戳
func GetUnix() int64 {return time.Now().Unix()
}//获取当前日期
func GetDate() string  {template := "2006-01-02 15:04:05"return time.Now().Format(template)
}//获取年月日
func GetDay() string {template := "20060102"return time.Now().Format(template)
}func Md5(str string) string {data := []byte(str)return fmt.Sprintf("%x\n", md5.Sum(data))
}

(4).增加路由配置

package routersimport ("gindemo/controllers/admin""gindemo/middlewares""github.com/gin-gonic/gin"
)//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {//路由分组: 配置全局中间件:middlewares.InitMiddlewareadminRouters := r.Group("/admin", middlewares.InitMiddleware){//单文件上传adminRouters.POST("/user/doUpload", admin.UserController{}.DoUpload)  //不同名字的多文件上传adminRouters.GET("/user/addnotfilenameupload",admin.UserController{}.AddNotFileNameUpload)//不同文件名的多文件上传adminRouters.POST("/user/doNotFileNameUploads", admin.UserController{}.DoNotFileNameUploads)//相同名字的多文件上传adminRouters.GET("/user/addcommonfilenameupload",admin.UserController{}.AddCommonFileNameUpload)//相同文件名的多文件上传adminRouters.POST("/user/doCommonFileNameUploads", admin.UserController{}.DoCommonFileNameUploads)      }//添加:按日期单文件上传adminRouters.GET("/user/addByDate",admin.UserController{}.AddByDate)//添加:按日期单文件上传adminRouters.POST("/user/doUploadByDate", admin.UserController{}.DoUploadByDate)
}

[上一节][golang gin框架] 3.路由详解,自定义控制器,中间件

[下一节]golang gin框架] 5.Cookie以及Session