一.Gin 中自定义 Model
关于 Model
如果应用非常简单的话,我们可以在 Controller 里面处理常见的业务逻辑,但是如果
有一个功能想在多个控制器、或者多个模板里面复用的话,那么就可以把公共的功
能单独抽取出来作为一个模块(Model),Model 是逐步抽象的过程,一般会在 Model
里面封装一些公共的方法让不同 Controller 使用,也可以在 Model 中实现和数据库打交道
2.Model 里面封装公共的方法
新建 models/ tools.go
package models
import (
"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 main
import (
"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 itying
import (
_ "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 models
import (
"crypto/md5"
)
func Md5(str string) string {
data := []byte(str)
return fmt.Sprintf("%x\n", md5.Sum(data))
}
方法二
package models
import (
"crypto/md5"
)
func Md5(str string) string {
h := md5.New()
io.WriteString(h, str)
return fmt.Sprintf("%x\n", h.Sum(nil))
}
二.Gin 文件上传
注意:需要在上传文件的 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 MiB
router.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 admin
import (
"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) {
//获取表单中提交的username
username := 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 routers
import (
"gindemo/controllers/admin"
"gindemo/middlewares"
"github.com/gin-gonic/gin"
)
//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := 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 routers
import (
"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) {
//获取表单中提交的username
username := 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 routers
import (
"gindemo/controllers/admin"
"gindemo/middlewares"
"github.com/gin-gonic/gin"
)
//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := 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 routers
import (
"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) {
//获取表单中提交的username
username := c.PostForm("username")
//获取form
form,_ := 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 routers
import (
"gindemo/controllers/admin"
"gindemo/middlewares"
"github.com/gin-gonic/gin"
)
//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := 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 routers
import (
"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) {
//获取表单中提交的username
username := 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,.jpeg
extName := path.Ext(file.Filename)
//设置后缀map
allowExtMap := 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 models
import (
"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 routers
import (
"gindemo/controllers/admin"
"gindemo/middlewares"
"github.com/gin-gonic/gin"
)
//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := 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)
}