在Go语言中,使用Gin框架处理文件上传是一个相对直接的过程。Gin框架内置了对multipart/form-data类型请求的支持,这是Web上传文件时常用的编码类型。下面是一个简单的示例,展示如何使用Gin来接收并保存上传的文件。
1、单文件上传
官方示例重现
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 设置上传的路由和处理函数
r.POST("/upload", func(c *gin.Context) {
// 单文件上传
file, err := c.FormFile("file") //此方法会返回一个multipart.FileHeader对象,包含了上传文件的相关信息
if err != nil {
c.String(400, fmt.Sprintf("上传文件出错:%s", err.Error()))
return
}
// 指定文件保存的路径,
dst := "./uploads/" + file.Filename
// 保存上传的文件到指定的目录,你需要确保该目录事先已存在,否则会引发错误
err = c.SaveUploadedFile(file, dst)
if err != nil {
c.String(500, fmt.Sprintf("保存文件出错:%s", err.Error()))
return
}
c.String(200, fmt.Sprintf("文件'%s'上传成功!", file.Filename))
})
// 启动服务器
//然后,你可以使用Postman或者curl等工具向http://localhost:8080/upload发送POST请求,并附上要上传的文件(确保Content-Type设置为multipart/form-data且表单字段名与代码中一致,这里是"file")
r.Run(":8080")
}
注意事项
- 确保服务器有权限写入指定的文件保存目录。
- 根据需要处理文件大小限制、文件类型验证等安全措施,以防止恶意文件上传。
- 实际应用中可能还需要考虑并发访问、文件命名冲突等问题。
2、项目中实现单文件上传
(1)、项目结构
(2)、定义模板
需要在上传文件的 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 }}
(3)、定义业务控制类
package admin
import (
"github.com/gin-gonic/gin"
"net/http"
"path"
)
type UserController struct {
}
// Add 添加:单文件上传
// http://localhost:8080/admin/user/Add
func (con UserController) Add(c *gin.Context) {
c.HTML(http.StatusOK, "admin/user/add.html", gin.H{
"title": "单文件上传",
})
}
// DoUpload 单文件上传
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,
})
}
(4)、定义路由配置
package routers
import (
"gin-mall/controllers/admin"
"gin-mall/middlewares"
"github.com/gin-gonic/gin"
)
// 设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := r.Group("/admin", middlewares.InitMiddleware)
{
adminRouters.GET("user/Add", admin.UserController{}.Add)
//单文件上传
adminRouters.POST("/user/DoUpload", admin.UserController{}.DoUpload)
}
}
(5)、主入口
package main
import (
"gin-mall/routers"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/**/**/*")//templates下有多少层目录就有多少个"/**"
routers.AdminRoutersInit(r)
r.Run(":8080")
}
3、项目中实现多文件上传(同名覆盖)
(1)、项目结构
(2)、定义模板
<!-- 相当于给模板定义一个名字, 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 }}
(3)、定义业务控制类
package admin
import (
"github.com/gin-gonic/gin"
"net/http"
"path"
)
type UserController struct {
}
// 添加:不同名字的多文件上传
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,
})
}
(4)、定义路由配置
package routers
import (
"gin-mall/controllers/admin"
"gin-mall/middlewares"
"github.com/gin-gonic/gin"
)
// 设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := r.Group("/admin", middlewares.InitMiddleware)
{
// //不同名字的多文件上传
adminRouters.GET("user/AddNotFileNameUpload", admin.UserController{}.AddNotFileNameUpload)
//不同文件名的多文件上传
adminRouters.POST("/user/DoNotFileNameUploads", admin.UserController{}.DoNotFileNameUploads)
}
}
(5)、主入口
package main
import (
"gin-mall/routers"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/**/**/*")//templates下有多少层目录就有多少个"/**"
routers.AdminRoutersInit(r)
r.Run(":8080")
}
4、项目中实现多文件上传
https://gin-gonic.com/zh-cn/docs/examples/upload-file/multiple-file/
(1)、项目结构
跟第3点一样结构
(2)、定义模板
<!-- 相当于给模板定义一个名字, 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 }}
(3)、定义业务控制类
package admin
import (
"github.com/gin-gonic/gin"
"net/http"
"path"
)
type UserController struct {
}
// 添加:相同名字的多文件上传
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": "文件上传成功",
})
}
(4)、定义路由配置
package routers
import (
"gin-mall/controllers/admin"
"gin-mall/middlewares"
"github.com/gin-gonic/gin"
)
// 设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := r.Group("/admin", middlewares.InitMiddleware)
{
//相同名字的多文件上传
adminRouters.GET("/user/addcommonfilenameupload", admin.UserController{}.AddCommonFileNameUpload)
//相同文件名的多文件上传
adminRouters.POST("/user/doCommonFileNameUploads", admin.UserController{}.DoCommonFileNameUploads)
}
}
(5)、主入口
package main
import (
"gin-mall/routers"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/**/**/*")//templates下有多少层目录就有多少个"/**"
routers.AdminRoutersInit(r)
r.Run(":8080")
}
5、文件上传 按照日期存储
(1)、定义模板
<!-- 相当于给模板定义一个名字, 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 admin
import (
"gin-mall/utils"
"github.com/gin-gonic/gin"
"net/http"
"os"
"path"
"strconv"
)
type UserController struct {
}
// 添加:按日期单文件上传
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 := utils.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(utils.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)、定义路由配置
package routers
import (
"gin-mall/controllers/admin"
"gin-mall/middlewares"
"github.com/gin-gonic/gin"
)
// 设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := r.Group("/admin", middlewares.InitMiddleware)
{
//添加:按日期单文件上传
adminRouters.GET("/user/AddByDate", admin.UserController{}.AddByDate)
//添加:按日期单文件上传
adminRouters.POST("/user/DoUploadByDate", admin.UserController{}.DoUploadByDate)
}
}