1. Gin官方文档中的单文件上传功能
-
定义gin的一个Engine实例,它是gin框架的实例,包含muxer、中间件和配置设置;这里已连接Logger和Recovery中间件;
-
设置MaxMultipartMemory:可以对multipart forms设置最大存储限制;
-
使用gin.Context中的FormFile方法,绑定上传的文件;
-
SaveUploadedFile方法用于文件保存;
后端部分代码:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"path/filepath"
)
func main() {
// Default() 返回一个Engine实例,该实例已连接Logger和Recovery中间件
router := gin.Default()
// 对multipart forms设置一个较低的存储设置 默认是32MiB
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// form参数
name := c.PostForm("name") // 用户名
email := c.PostForm("email") // 邮箱
file, err := c.FormFile("file") // 绑定文件 文件参数名为file
if err != nil {
c.String(http.StatusBadRequest, "get form err: %s", err.Error()) // 400 参数绑定错误
return
}
// Base 返回路径的最后一个元素。在提取最后一个元素之前,将删除尾随路径分隔符。
filename := filepath.Base(file.Filename)
// SaveUploadedFile 将form文件上传到特定dst
if err := c.SaveUploadedFile(file, filename); err != nil {
c.String(http.StatusBadRequest, "upload file err: %s", err.Error())
return
}
c.String(http.StatusOK, "File %s uploaded successfully with fields name=%s and email=%s.", file.Filename, name, email)
})
router.Run(":8888") // port
}
postman测试:
2. 定义结构体,使用multipart包绑定multipart form中文件
绑定数据的结构体:
使用multipart.FileHeader绑定文件,type为blob
type FileUpload struct {
Name string `form:"name" json:"name" binding:"-"`
Email string `form:"email" json:"email" binding:"-"`
File multipart.FileHeader `form:"file" type:"blob" binding:"required"`
}
后端代码:
func main() {
// Default() 返回一个Engine实例,该实例已连接Logger和Recovery中间件
router := gin.Default()
// 对multipart forms设置一个较低的存储设置 默认是32MiB
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
var req FileUpload
err := c.ShouldBind(&req)
if err != nil {
c.String(http.StatusBadRequest, "get form err: %s", err.Error()) // 400 参数绑定错误
return
}
// 获取源文件File
src, err := req.File.Open() // 打开并返回FileHeader的相关文件。
if err != nil {
c.String(http.StatusBadRequest, "get form file err: %s", err.Error())
return
}
defer src.Close()
// 创建存储的目标文件
path := "./" + req.File.Filename
dst, err := os.Create(path)
if err != nil {
c.String(http.StatusBadRequest, "create file err: %s", err.Error())
return
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
c.String(http.StatusBadRequest, "upload file err: %s", err.Error())
return
}
c.String(http.StatusOK, "File %s uploaded successfully with fields name=%s and email=%s.", req.File.Filename, req.Name, req.Email)
})
router.Run(":8888")
}
postman测试:
3. mime/multipart包简要讲解
-
mime/multipart是Go语言标准库中的一个包,用于处理multipart类型的MIME(Multipurpose Internet Mail Extensions)消息。它提供了一组类型和函数,用于创建、解析和操作multipart消息。
-
mime/multipart包广泛用于处理HTTP请求和响应中的multipart/form-data类型的数据,例如文件上传和表单提交。
-
为了防止恶意输入,该包对其处理的MIME数据的大小设置了限制。Reader.NextPart和Reader.NextRawPart将一个部分的头文件数量限制为10000,Reader.ReadForm将所有FileHeaders的头文件总数限制为10000,这些限制可以通过GODEBUG=multipartmaxheaders=<values>设置来调整。Reader.ReadForm进一步限制表格中的部分数量为1000,这个限制可以通过GODEBUG=multipartmaxparts=<value>设置来调整。
主要类型介绍:
File:
接口定义了从文件读取数据的方法,用于在multipart消息中添加文件部分。它的内容可以存储在内存中或磁盘上。如果存储在磁盘上,File的底层具体类型将是一个*os.File。该接口的定义如下:
type File interface {
io.Reader
io.ReaderAt
io.Seeker
io.Closer
}
该接口包含了io.Reader、io.ReaderAt、io.Seeker和io.Closer等接口的方法
FileHeader:
type FileHeader struct {
Filename string
Header textproto.MIMEHeader
Size int64
// contains filtered or unexported fields
}
该结构体描述了一个multipart请求的文件部分内容,包括文件名Filename,标准MIME头部,文件大小(单位为字节);
该结构体有一个Open()的方法,用于返回对应的相关文件;
Form:
Form是一个经解析的multipart form。它的文件部分是*FileHeader结构,用它的Open()方法获取文件。
它的值部分以字符串形式存储。两者都是通过字段名来键入的。
type Form struct {
Value map[string][]string
File map[string][]*FileHeader
}
它有一个RemoveAll()方法,用于删除与Form相关的任何临时文件。
Reader:
Reader是一个迭代器,用于读取MIME mulltipart body。使用func NewReader(r io.Reader, boundary string) *Reader
函数进行实例化,boundary为分界符。主要方法如下:
-
NextPart():读取multipart中的下一个Part,并返回其结构。当没有更多的part时,会返回错误io.EOF;
-
NextRawPart():返回multipart中的下一个part或一个错误。当没有更多的部分时,会返回错误io.EOF;它与NextPart区别在于与没有对 "Content-Transfer-Encoding: quoted-printable "的特殊处理;
-
ReadForm():用于解析整个multipart的信息,这些部分的Content-Disposition为 "form-data"。
Writer:
用于生成mulipart消息,使用func NewWriter(w io.Writer) *Writer
函数进行实例化,写入w,使用随机的分界符(boundary)。
后续可以使用func (w *Writer) Boundary() string
获取Writer的分界符boundary,使用func (w *Writer) SetBoundary(boundary string) error
函数进行boundary设置。
-
使用
func (w *Writer) Close() error
函数介绍multipart消息的写入。 -
CreatePart()方法:
func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error)
根据提供的header(textproto.MIMEHeader类型)创建一个新的multipart部分的part。该part的body应该被写到返回的Writer中。在调用CreatePart之后,以前的任何部分都不能再被写入。 -
CreateFormField()方法:
func (w *Writer) CreateFormField(fieldname string) (io.Writer, error)
使用给定的字段名调用带有头的CreatePart。