Skip to content

upload

在 Go 的 Gin 框架中,处理文件上传是一个常见的需求。Gin 提供了简便的 API 来处理文件上传,支持单文件上传和多文件上传。

1. 基本的文件上传

1.1 单个文件上传

以下是一个最简单的示例,展示如何使用 Gin 接收单个文件并保存到本地磁盘。

go
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	// 上传文件路由
	r.POST("/upload", func(c *gin.Context) {
		// 获取表单中的文件,"file" 是上传文件的字段名称
		file, err := c.FormFile("file")
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": "File upload failed",
			})
			return
		}

		// 保存文件到指定路径
		err = c.SaveUploadedFile(file, "./uploads/"+file.Filename)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": "Unable to save file",
			})
			return
		}

		// 返回成功消息
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("File %s uploaded successfully", file.Filename),
		})
	})

	// 启动服务
	r.Run(":8080")
}

1.2 解释

  • c.FormFile("file"):从请求的表单数据中获取上传的文件,file 是前端表单中 <input type="file" name="file"> 的字段名。
  • c.SaveUploadedFile(file, "./uploads/"+file.Filename):将上传的文件保存到指定路径。这里的 file.Filename 是客户端上传的文件名,建议你确保上传路径是安全的,并且该文件名不会覆盖已有文件。

1.3 前端 HTML 表单

前端的文件上传表单示例:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
        <label for="file">Choose a file:</label>
        <input type="file" name="file" id="file" required>
        <button type="submit">Upload</button>
    </form>
</body>
</html>

2. 多个文件上传

Gin 也支持处理多个文件上传。通过 c.MultipartForm() 方法可以获取多个文件。

2.1 多个文件上传示例

go
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	// 上传多个文件路由
	r.POST("/upload_multiple", func(c *gin.Context) {
		// 获取多个文件
		form, err := c.MultipartForm()
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"error": "Unable to get form data",
			})
			return
		}

		// 获取表单中的所有文件
		files := form.File["files"]
		for _, file := range files {
			// 保存每个文件
			err := c.SaveUploadedFile(file, "./uploads/"+file.Filename)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{
					"error": "Unable to save file",
				})
				return
			}
		}

		// 返回成功消息
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("%d files uploaded successfully", len(files)),
		})
	})

	// 启动服务
	r.Run(":8080")
}

2.2 前端 HTML 表单(多个文件)

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Multiple File Upload</title>
</head>
<body>
    <form action="http://localhost:8080/upload_multiple" method="post" enctype="multipart/form-data">
        <label for="files">Choose files:</label>
        <input type="file" name="files" id="files" multiple required>
        <button type="submit">Upload</button>
    </form>
</body>
</html>

3. 文件大小限制

为了防止上传过大的文件影响服务器性能,Gin 提供了设置文件大小限制的功能。在启动 Gin 引擎时,可以通过 gin.Default() 配置最大文件大小。

3.1 设置最大文件大小限制

go
r := gin.Default()

// 设置最大上传文件大小为 10 MB
r.MaxMultipartMemory = 10 << 20  // 10 MB

r.POST("/upload", func(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "File upload failed",
		})
		return
	}
	
	// 保存文件
	err = c.SaveUploadedFile(file, "./uploads/"+file.Filename)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Unable to save file",
		})
		return
	}

	// 返回成功消息
	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("File %s uploaded successfully", file.Filename),
	})
})

这样就设置了最大文件上传大小为 10 MB,超过此大小的文件会触发 http.StatusRequestEntityTooLarge 错误。

4. 处理上传的文件类型

如果你只希望接收某些类型的文件(例如只接受图片文件),可以通过 mime.TypeByExtension() 来验证文件类型。

4.1 限制文件类型

go
r.POST("/upload", func(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "File upload failed",
		})
		return
	}

	// 检查文件类型
	ext := file.Filename[len(file.Filename)-4:]
	if ext != ".jpg" && ext != ".png" && ext != ".jpeg" {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Invalid file type, only .jpg, .jpeg, .png are allowed",
		})
		return
	}

	// 保存文件
	err = c.SaveUploadedFile(file, "./uploads/"+file.Filename)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Unable to save file",
		})
		return
	}

	// 返回成功消息
	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("File %s uploaded successfully", file.Filename),
	})
})

5. 上传进度和反馈

Go 本身没有直接提供上传进度的 API,但是你可以通过流式传输和一些中间件来实现上传进度的反馈。可以通过定期检查文件大小和已上传字节数来实现进度条。

6. 总结

  • 单文件上传:通过 c.FormFile() 获取上传的文件,并使用 c.SaveUploadedFile() 保存文件。
  • 多文件上传:通过 c.MultipartForm() 获取上传的多个文件。
  • 文件大小限制:使用 r.MaxMultipartMemory 设置文件上传的最大大小。
  • 文件类型检查:通过文件后缀名或 MIME 类型来限制允许上传的文件类型。