Gin框架: 通用方法的封装与上传功能的应用

通用方法模块的设计


  • 通用的方法可以定义在一个模块里,以方便调用,无需重复造轮子
  • 一般可以设计一个顶层的 models 包来承载公共方法

models 包

package modelsimport ("crypto/md5""encoding/hex""math/rand""time""fmt""io"
)// 通用时间模板定义
var goTime = "2006-01-02 15:04:05"
var shortGoTime = "2006-01-02"
var md5Salt = GenerateSalt(16) // 加密时用的盐// 时间戳转换成日期
func UnixToTime(timestamp int) string {t := time.Unix(int64(timestamp), 0)return t.Format(goTime)
}// 日期转换成时间戳
func DateToUnix(str string) int64 {t, err := time.ParseInLocation(goTime, str, time.Local)if err != nil {return 0}return t.Unix()
}// 获取时间戳
func GetUnix() int64 {return time.Now().Unix()
}// 获取当前的日期
func GetDate() string {return time.Now().Format(goTime)
}// 获取年月日
func GetDateShort() string {return time.Now().Format(shortGoTime)
}// GenerateSalt 生成一个指定长度的随机盐  
func GenerateSalt(length int) string {const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))b := make([]byte, length)for i := range b {b[i] = charset[seededRand.Intn(len(charset))]}return string(b)
}// MD5Hash 计算并返回给定字符串的 MD5 哈希值
func Md5Hash(text string) string {hasher := md5.New()hasher.Write([]byte(text + md5Salt))md5Bytes := hasher.Sum(nil)return hex.EncodeToString(md5Bytes)
}// 第二种加密方式,和上述结果一致
func Md5Hash2(text string) string {md5str := fmt.Sprintf("%x", md5.Sum([]byte(text + md5Salt)))return md5str
}// 第三种加密方法
func Md5Hash3(text string) string {hasher := md5.New()io.WriteString(hasher, text + md5Salt)md5str := fmt.Sprintf("%x", hasher.Sum(nil))return md5str
}// VerifyMD5Hash 验证给定的明文密码是否与存储的哈希值匹配
func VerifyMD5Hash(plainPassword, storedHash string) bool {return storedHash == Md5Hash(plainPassword)
}
  • 上述的方法,可以写在程序里,通过models来调用,也可配置全局,在模板里调用

main.go 主程序

package mainimport ("gin-demo/routers" // gin-demo 是 go mod init 初始化的工程,下同"github.com/gin-gonic/gin""html/template""gin-demo/models"
)func main() {// 创建一个默认的路由引擎r := gin.Default()// 自定义模板函数  注意要把这个函数放在加载模板前 全局配置r.SetFuncMap(template.FuncMap{"UnixToTime": models.UnixToTime,"DateToUnix": models.DateToUnix,"GetUnix": models.GetUnix,"GetDate": models.GetDate,"GetDateShort": models.GetDateShort,})// 加载模板 放在配置路由前面r.LoadHTMLGlob("tpls/**/*")routers.WebRoutersInit(r)routers.ApiRoutersInit(r)routers.AdminRoutersInit(r)r.Run()
}

web首页控制器

package webimport ("gin-demo/models""net/http""github.com/gin-gonic/gin"
)type WebCtrl struct{}func (con WebCtrl) Index(c *gin.Context) {// 验证加密和验证originStr := "dsdssdsdsfdsdsfss" // 原字符md5HashStr := models.Md5Hash(originStr) // 加密后的字符串checkMd5Result := models.VerifyMD5Hash(originStr, md5HashStr) // 加密后的验证c.HTML(http.StatusOK, "web/index.html", gin.H{"msg": "我是一个msg","t":   1760025600,"d": "2025-10-10 00:00:00","originStr": originStr, // 原字符串"md5HashStr": md5HashStr, // md5 加密后的字符串"checkMd5Result": checkMd5Result, // 验证是否匹配"testMd5_2": models.Md5Hash2(originStr), // 第二种加密结果"testMd5_3": models.Md5Hash3(originStr), // 第三种加密结果})
}

web首页模板 tpls/web/index.html

{{ define "web/index.html" }}<h3>web index 页面</h3>{{.msg}}<h3> UnixToTime 时间处理</h3>{{ UnixToTime .t }} <br />{{ .t | UnixToTime }}<h3> DateToUnix 时间处理</h3>{{ .d | DateToUnix }} <br />{{ DateToUnix .d }}<h3> GetUnix 时间处理</h3>{{ GetUnix }} <br /><h3> GetDate 时间处理</h3>{{ GetDate }}<h3> GetUnix 时间处理</h3>{{ GetDateShort }}<h3> md5 原字符串 </h3>{{ .originStr }}<h3> md5 加密后的hash </h3>{{ .md5HashStr }}<h3> md5 验证是否相同 </h3>{{ .checkMd5Result }}<h3> md5 第二种md5加密方式得到的字符串 </h3>{{ .testMd5_2 }}<h3> md5 第三种md5加密方式得到的字符串 </h3>{{ .testMd5_3 }}{{ end }}

效果图

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

上传功能


这里上传分多种场景:上传单张图片,上传多张图片
在上传多张图片中又分为,同一文件字段和不同文件字段的处理

1 ) 设计路由

package routersimport ("gin-demo/controllers/admin""gin-demo/middlewares" // 引入"github.com/gin-gonic/gin"
)func AdminRoutersInit(r *gin.Engine) {adminRouters := r.Group("/admin", middlewares.InitMiddleware) // 注意这里{adminRouters.GET("/", admin.IndexCtrl{}.Index)adminRouters.GET("/user", admin.UserCtrl{}.Index)adminRouters.GET("/user/success", admin.UserCtrl{}.Success)adminRouters.GET("/user/error", admin.UserCtrl{}.Error)adminRouters.GET("/user/upload", admin.UserCtrl{}.Upload) // 这是单个上传页面adminRouters.GET("/user/uploads", admin.UserCtrl{}.Uploads) // 这个是多文件上传页面,在这个页面中有两个上传入口adminRouters.POST("/user/doUpload", admin.UserCtrl{}.DoUpload) // 处理单个上传adminRouters.POST("/user/doUploads", admin.UserCtrl{}.DoUploads) // 处理多个上传(同一字段)adminRouters.POST("/user/doUploads2", admin.UserCtrl{}.DoUploads2) // 处理多个上传(不同字段)}
}

2 ) main 文件中对上传路径和模板的处理

package mainimport ("gin-demo/routers" // gin-demo 是 go mod init 初始化的工程,下同"github.com/gin-gonic/gin""html/template""gin-demo/models"
)func main() {// 创建一个默认的路由引擎r := gin.Default()// 自定义模板函数  注意要把这个函数放在加载模板前 全局配置r.SetFuncMap(template.FuncMap{"UnixToTime": models.UnixToTime,"DateToUnix": models.DateToUnix,"GetUnix": models.GetUnix,"GetDate": models.GetDate,"GetDateShort": models.GetDateShort,})// 加载模板 放在配置路由前面r.LoadHTMLGlob("tpls/**/**/*")// 配置静态web目录   第一个参数表示路由, 第二个参数表示映射的目录r.Static("/static", "./static")routers.WebRoutersInit(r)routers.ApiRoutersInit(r)routers.AdminRoutersInit(r)r.Run()
}

注意了,这个之前的项目中,首页模板修改成 tpls/web/home/index.html 就不会有问题了 (这个可忽略,因为不涉及)

主要是这两个

// 加载模板 放在配置路由前面
r.LoadHTMLGlob("tpls/**/**/*")
// 配置静态web目录   第一个参数表示路由, 第二个参数表示映射的目录
r.Static("/static", "./static")

3 )HTML 模板文件处理

3.1 单个上传

{{ define "admin/user/upload.html" }}<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>{{ end }}

3.2 多个上传

{{ define "admin/user/uploads.html" }}<h2>演示多文件上传:相同文件字段</h2><form action="/admin/user/doUploads" 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>头 像3:<input type="file" name="face[]" /><br> <br><input type="submit" value="提交"></form><br /><hr /><br /><h2>演示多文件上传:不同的文件字段</h2><form action="/admin/user/doUploads2" 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>{{ end }}

4 ) 上传控制器

package adminimport ("gin-demo/models""mime/multipart""net/http""os""path""strconv""time""math/rand""github.com/gin-gonic/gin"
)type UserCtrl struct {BaseCtrl
}// 获取 单一上传 页面
func (con UserCtrl) Upload(c *gin.Context) {c.HTML(http.StatusOK, "admin/user/upload.html", gin.H{})
}// 获取 多上传 页面
func (con UserCtrl) Uploads(c *gin.Context) {c.HTML(http.StatusOK, "admin/user/uploads.html", gin.H{})
}// 允许的后缀判断
func checkAllowedExt(extName string, c *gin.Context) bool {allowExtMap := map[string]bool{ ".jpg": true, ".png": true, ".gif": true, ".jpeg": true, }if _, ok := allowExtMap[extName]; !ok {return false}return true
}// 获取文件路径
func saveFile(file *multipart.FileHeader, c *gin.Context) string {fileName := file.FilenameextName := path.Ext(fileName) // 获取后缀// 判断文件后缀名是否正确 .jpg .png .gif .jpegif (!checkAllowedExt(extName, c)) {c.String(200, "文件类型不合法")return ""}// 处理文件存储路径day := models.GetDateShort()dir := "./static/upload/" + day// 0777用来测试 生产环境应使用 0666 让其不可执行,保证安全性if err := os.MkdirAll(dir, 0777); err != nil {// 处理错误return ""}source := rand.NewSource(time.Now().UnixNano()) // 初始化随机数种子rnd := rand.New(source) // 使用新的随机数源创建一个随机数生成器random10DigitNumber := rnd.Intn(9000000000) + 1000000000 // 生成一个10位的随机整数  // 没有错误 生成文件名称 144325235235_xxxxxxxxxx.pngfileUnixName := strconv.FormatInt(models.GetUnix(), 10) + "-" + strconv.Itoa(random10DigitNumber)//static/upload/20200623/144325235235.pngsavedDir := path.Join(dir, fileUnixName + extName)// 存储c.SaveUploadedFile(file, savedDir)return savedDir
}// 处理 单一上传 提交
func (con UserCtrl) DoUpload(c *gin.Context) {// 获取表单username := c.PostForm("username") // 获取字段file, err := c.FormFile("face") // 获取文件if err != nil {// 处理错误c.String(http.StatusOK, "文件错误:%v", err)return}// 保存文件位置dst := saveFile(file, c)if dst == "" {return}// 响应jsonc.JSON(http.StatusOK, gin.H{"success":  true,"username": username,"dst":      dst,})
}// 处理 多上传 提交:相同字段
func (con UserCtrl) DoUploads(c *gin.Context) {// 获取表单username := c.PostForm("username")form, _ := c.MultipartForm()// 处理文件files := form.File["face[]"]// 没有文件的校验if (len(files) == 0) {// 做一些处理c.String(http.StatusOK, "没上传一张图片!")return}var dsts []string// 处理文件for _, file := range files {// 进入存储流程dst := saveFile(file, c)if (dst == "") {continue}dsts = append(dsts, dst)}c.JSON(http.StatusOK, gin.H{"success":  true,"username": username,"dst": dsts,})
}// 处理 多上传 提交:不同字段
func (con UserCtrl) DoUploads2(c *gin.Context) {// 表单处理username := c.PostForm("username")file1, err1 := c.FormFile("face1")file2, err2 := c.FormFile("face2")// 错误处理:上传文件到指定的目录if !(err1 == nil || err2 == nil) {c.String(200, "没有上传文件")return;}// 保存文件var dsts []string// 处理文件if file1 != nil {dst1 := saveFile(file1, c)if (dst1 != "") {dsts = append(dsts, dst1)}}if file2 != nil {dst2 := saveFile(file2, c)if (dst2 != "") {dsts = append(dsts, dst2)}}// 响应c.JSON(http.StatusOK, gin.H{"success":  true,"username": username,"dst": dsts,})
}

5 ) 聊聊注意事项

  • 上传文件注意 html 模板中设置 enctype="multipart/form-data" 这个是关键
  • 上传时,如果有文件服务器,需要上传到文件服务器中,这里只演示本服务器上传基础功能
  • 上传图片时的路径处理,包括按照时间来创建目录和文件
  • 这里仅是一个示例程序,用于学习和交流,可以基于此来改造

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/692783.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习基础(三)监督学习的进阶探索

导语&#xff1a;上一节我们深入地探讨监督学习和非监督学习的知识&#xff0c;重点关注它们的理论基础、常用算法及实际应用场景&#xff0c;详情可见&#xff1a; 机器学习基础&#xff08;二&#xff09;监督与非监督学习-CSDN博客文章浏览阅读769次&#xff0c;点赞15次&a…

strings.xml补充知识

复数名词 <plurals name"book"><item name"one">book</item><item name"others">books</item> </plurals>int bookCount 4; Resources res getResources(); String bookCount res.getQuantityString(R.…

webpack打包速度优化思维导图

webpack打包速度优化思维导图 前言附件 前言 去年的时候公司一个项目体积过大&#xff0c;我是m1芯片的macpro&#xff0c;光启动就要1分钟&#xff0c;配置差点都电脑&#xff0c;启动就要3分钟&#xff0c;自然打包速度也会慢很多&#xff0c;我们是gitlab设置成了自动打包的…

phar反序列化原理及利用

phar是什么&#xff1f; phar 是 PHP 的一种归档文件格式&#xff0c;类似于 ZIP 或 TAR 文件&#xff0c;它可以包含多个文件和目录&#xff0c;并且可以像访问普通文件系统一样在 PHP 中进行访问。在php 5.3 或更高版本中默认开启 在php.ini中配置如下时&#xff0c;才能生成…

Latent Diffusion Models / Stable Diffusion(LDM)

High-Resolution Image Synthesis with Latent Diffusion Models&#xff08;CVPR 2022&#xff09;https://arxiv.org/abs/2112.10752latent-diffusionstable-diffusion cross-attention&#xff08;用于多模态任务&#xff09; Cross-attention vs Self-attention&#xff…

iOS整理 - 关于直播 - 搭建服务端

前言 其实本人一直都想自己简单做一套直播&#xff08;包括移动端和服务端&#xff09;的开发测试&#xff0c;但是之前一直做得比较迷茫。最近偶然间在来了灵感&#xff0c;瞬间解除了我很多疑惑。我会分享出来&#xff0c;希望大家一起研究下。稍后&#xff0c;我完整做好了…

《Solidity 简易速速上手小册》第10章:区块链项目实战(2024 最新版)

文章目录 10.1 分析真实的 Solidity 项目10.1.1 基础知识解析进一步的知识探索实际操作技巧 10.1.2 重点案例&#xff1a;去中心化预测市场案例 Demo&#xff1a;创建去中心化预测市场案例代码PredictionMarket.sol - 智能合约前端界面 测试和验证拓展功能 10.1.3 拓展案例 1&a…

Leetcode 1089.复写零

目录 题目 思路 代码 题目 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不要从函数返回…

ETL、ELT区别以及如何正确运用

一、 浅谈ETL、ELT ETL与ELT的概念 ETL (Extract, Transform, Load) 是一种数据集成过程&#xff0c;通常用于将数据从一个或多个源系统抽取出来&#xff0c;经过清洗、转换等处理后&#xff0c;加载到目标数据存储中。这种方法适用于需要对数据进行加工和整合后再加载到目标…

MySQL 锁的内存结构

目录 1.摘要 2. 加锁的本质 3. 锁结构详解 3.1 锁所在的事务信息 3.2 索引信息 3.3 表锁/行锁信息 3.4 type_mode 3.5 其他信息 3.6 一堆比特位 1.摘要 在 MySQL 中&#xff0c;有很多种锁&#xff0c;例如行锁&#xff0c;表锁&#xff0c;页锁&#xff0c;全局锁&a…

MyBatis Plus:自定义typeHandler类型处理器

目录 引言&#xff1a;关于TypeHandler PostGreSQL&#xff1a;JSON数据类型 PostGreSQL数据库驱动&#xff1a;PGobject类 TypeHandler类型处理器 自定义类型处理器 类型处理器实现&#xff1a;PGJsonTypeHandler 注册类型处理器 引言&#xff1a;关于TypeHandler MyBa…

Sui在Dacade推出Move课程,完成学习奖励SUI

Dacade推出了一门Sui开发者课程&#xff0c;通过一系列引人入胜的挑战&#xff0c;为开发者提供了一个沉浸式的Move技术之旅。在这门课程中&#xff0c;Dacade的教育材料将引导用户利用Sui强大的DeFi原生功能&#xff08;包括DeepBook和zkLogin&#xff09;构建DeFi应用。此外&…

提升VR全景摄影画质的8个因素

如今VR全景拍摄的门槛已经很低&#xff0c;包括无人机、全景相机等都具有一键全景的功能。很多初次接触VR全景拍摄的朋友会发现同样的设备&#xff0c;为啥拍出来的效果就不如别人呢&#xff1f; 其实&#xff0c;要提升VR全景拍摄质量&#xff0c;只需要了解以下几个环节&…

基于多种机器学习模型的西北地区蒸散发模拟与趋势分析_季鹏_2023

基于多种机器学习模型的西北地区蒸散发模拟与趋势分析_季鹏_2023 摘要关键词 1 资料和方法1. 1 研究区域与观测数据1. 2 机器学习模型构建与验证方法1. 3 SHAP 可解释性方法 2 主要结果2. 1 不同模型的模拟性能和泛化能力2. 2 不同模型的可解释性分析2. 3 5 km 分辨率格点蒸散发…

鸿蒙Next怎么升级,有便捷的方法?

早在2023年11月&#xff0c;市场上有自媒体博主表示&#xff0c;华为HarmonyOS NEXT的升级计划是2X年底到2X年初完成一亿部&#xff0c;2X年底完成三亿部。虽然该博主没有明确具体年份&#xff0c;但预计是2024年底2025年初升级一亿部HarmonyOS NEXT设备&#xff0c;2025年底完…

上门服务小程序系统|多元化服务和高效便捷的服务体验

现代社会的快节奏生活让人们越来越追求便捷与效率&#xff0c;而上门服务系统应运而生&#xff0c;成为了现代人生活中的新选择。通过在家就能享受各种服务&#xff0c;不仅省时省力&#xff0c;还能提供个性化的服务体验。 上门服务系统的出现&#xff0c;使得各类家政、维修…

盲盒小程序开发,线上盲盒平台的发展潜力

盲盒的出现给大众带来了全新的消费体验&#xff0c;目前&#xff0c;盲盒经济也是席卷了当代年轻人&#xff0c;一种新的商业模式就此出现。盲盒的玩法、种类也在不断创新进化&#xff0c;成为了吸引大众的消费形式。 当然&#xff0c;在当下盲盒稳步发展时期&#xff0c;也要…

Collection集合体系(ArrayList,LinekdList,HashSet,LinkedHashSet,TreeSet,Collections)

目录 一.Collection 二.List集合 三.ArrayList集合 四.LinkedList集合 五.Set集合 六.hashSet集合 七.LinkedHashSet集合 八.TreeSet集合 九.集合工具类Collections 集合体系概述 单列集合&#xff1a;Collection代表单列集合&#xff0c;每个元素&#…

onlyoffice基础环境搭建+部署+demo可直接运行 最简单的入门

office这个体系分为四个大教程 1、【document server文档服务器基础搭建】 2、【连接器(connector)或者jsApi调用操作office】-进阶 3、【document builder文档构造器使用】-进阶 4、【Conversion API(文档转化服务)】-进阶 如果需要连接器&#xff0c;可以查看&#xff1a;onl…

IO进程线程day4

1.思维导图 2.使用多进程完成两个文件的拷贝&#xff0c;父进程拷贝前一半&#xff0c;子进程拷贝后一半&#xff0c;父进程回收子进程的资源。 #include<myhead.h>int main(int argc, const char *argv[]) {//判断终端输入的参数是否合法if(argc!3){printf("input…