searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Gin文件上传与绑定及multipart包简要讲解

2023-06-29 02:20:05
378
0

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。

参考文档

Gin Web Framework (gin-gonic.com)

multipart package - mime/multipart - Go Packages

0条评论
0 / 1000
WSMG
3文章数
0粉丝数
WSMG
3 文章 | 0 粉丝
WSMG
3文章数
0粉丝数
WSMG
3 文章 | 0 粉丝
原创

Gin文件上传与绑定及multipart包简要讲解

2023-06-29 02:20:05
378
0

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。

参考文档

Gin Web Framework (gin-gonic.com)

multipart package - mime/multipart - Go Packages

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0