Gin从入门到精通 (七)文件上传和下载

文件上传和下载

1.文件上传

1.1单文件上传

在 Gin 中处理单文件上传,可以使用 c.FormFile 方法获取上传的文件,然后使用 c.SaveUploadedFile 方法保存文件。

go 复制代码
package main

import (
	"github.com/gin-gonic/gin"
	"log"
)

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

	r.POST("/upload", func(c *gin.Context) {
		// 获取名为 "file" 的上传文件
		file, err := c.FormFile("file")
		if err != nil {
			c.JSON(400, gin.H{"error": err.Error()})
			return
		}
		// 保存上传的文件
		if err := c.SaveUploadedFile(file, file.Filename); err != nil {
			log.Println("Failed to save file:", err)
			c.JSON(500, gin.H{"error": "Failed to save file"})
			return
		}

		c.JSON(200, gin.H{
			"message":  "File uploaded successfully",
			"filename": file.Filename,
		})
	})

	r.Run(":8080")
}

我们使用postman来提交,http://localhost:8080/upload

1.2多文件上传

处理多文件上传时,可以使用 c.MultipartForm 方法获取所有上传的文件。

go 复制代码
package main

import (
	"github.com/gin-gonic/gin"
	"log"
)

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

	r.POST("/multi-upload", func(c *gin.Context) {
		// 获取所有上传的文件
		form, err := c.MultipartForm()
		if err != nil {
			c.JSON(400, gin.H{"error": err.Error()})
			return
		}
		files := form.File["files"]

		for _, file := range files {
			// 保存每个上传的文件
			if err := c.SaveUploadedFile(file, file.Filename); err != nil {
				log.Println("Failed to save file:", err)
				c.JSON(500, gin.H{"error": "Failed to save file"})
				return
			}
		}

		c.JSON(200, gin.H{
			"message": "Files uploaded successfully",
			"count":   len(files),
		})
	})

	r.Run(":8080")
}

使用postman测试,http://localhost:8080/multi-upload

1.3 限制上传文件大小

Gin 默认允许上传 32 MiB 的文件,可通过 router.MaxMultipartMemory 调整:

go 复制代码
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"path/filepath"
	"strings"
)

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

	// 设置全局上传限制(可选)
	// 注意:Gin 默认的 MaxMultipartMemory 是 32 MiB
	r.MaxMultipartMemory = 10 << 20 // 10 MiB

	r.POST("/upload", func(c *gin.Context) {
		// 获取名为 "file" 的上传文件
		file, err := c.FormFile("file")

		// 校验文件大小(10MB = 10 * 1024 * 1024 bytes)
		maxSize := r.MaxMultipartMemory
		if file.Size > maxSize {
			c.JSON(http.StatusRequestEntityTooLarge, gin.H{
				"error": fmt.Sprintf("文件大小超过限制(最大 %dMB)", maxSize/(1<<20)),
			})
			return
		}

		if err != nil {
			c.JSON(400, gin.H{"error": err.Error()})
			return
		}
		//r.MaxMultipartMemory = 1 << 20 // 1 MiB
		// 保存上传的文件
		if err := c.SaveUploadedFile(file, file.Filename); err != nil {
			log.Println("Failed to save file:", err)
			c.JSON(500, gin.H{"error": "保存文件失败"})
			return
		}

		c.JSON(200, gin.H{
			"message":  "File uploaded successfully",
			"filename": file.Filename,
		})
	})

	r.Run(":8080")
}

1.4 限制上传文件类型

限制文件类型很简单,就是检测上传文件后缀 , 为了避免上传可执行文最好再校验MIME 类型

go 复制代码
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"path/filepath"
	"strings"
)

func isAllowedType(filename string) bool {
	allowed := map[string]bool{
		".jpg": true,
		".png": true,
		
	}
	ext := strings.ToLower(filepath.Ext(filename))
	return allowed[ext]
}

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

	// 设置全局上传限制(可选)
	// 注意:Gin 默认的 MaxMultipartMemory 是 32 MiB
	r.MaxMultipartMemory = 10 << 20 // 10 MiB

	r.POST("/upload", func(c *gin.Context) {
		// 获取名为 "file" 的上传文件
		file, err := c.FormFile("file")

		// 在接口中调用校验
		if !isAllowedType(file.Filename) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "不支持的文件类型"})
			return
		}
		if err != nil {
			c.JSON(400, gin.H{"error": err.Error()})
			return
		}
		//r.MaxMultipartMemory = 1 << 20 // 1 MiB
		// 保存上传的文件
		if err := c.SaveUploadedFile(file, file.Filename); err != nil {
			log.Println("Failed to save file:", err)
			c.JSON(500, gin.H{"error": "保存文件失败"})
			return
		}

		c.JSON(200, gin.H{
			"message":  "File uploaded successfully",
			"filename": file.Filename,
		})
	})

	r.Run(":8080")
}

1.5 避免上传文件名冲突

为了避免文件名冲突通常使用 UUID 或时间戳重命名,这里我们演示下 uuid

安装uuid库:

bash 复制代码
go get github.com/google/uuid

具体实现:

go 复制代码
		//<pre>  // 生成唯一文件名</pre>
		newFilename := uuid.New().String() + filepath.Ext(file.Filename)
		if err := c.SaveUploadedFile(file, newFilename); err != nil {
			log.Println("Failed to save file:", err)
			c.JSON(500, gin.H{"error": "保存文件失败"})
			return
		}

2.文件下载

2.1 单文件下载

下载很简单,使用 c.File()即可,下面是演示代码:

go 复制代码
package main

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

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

	// 文件下载
	r.GET("/download/:filename", func(c *gin.Context) {
		filename := c.Param("filename")
		filePath := "./img/" + filename

		if _, err := os.Stat(filePath); os.IsNotExist(err) {
			c.JSON(http.StatusNotFound, gin.H{"error": "文件不存在"})
			return
		}

		//c.Header("Content-Disposition", "attachment; filename="+filename)
		c.File(filePath)
	})

	r.Run(":8080")
}

2.2 设置浏览器头信息

如果你是用来图片文件,来测试上面的代码,你会发现浏览器是直接显示图片,而不是下载图片,所以我们需要使浏览器唤起下载行为。

通过 Content-Disposition 头强制浏览器下载文件(而非预览):

go 复制代码
c.Header("Content-Type", "application/octet-stream") // 表示是文件流,唤起浏览器下载,一般设置了这个,就要设置文件名
c.Header("Content-Disposition", "attachment; filename="+filename)  用来指定下载下来的文件名
c.Header("Content-Transfer-Encoding", "binary") // 表示传输过程中的编码形式,乱码问题可能就是因为它
c.File(filePath)
相关推荐
rkmhr_sef5 小时前
Go-Gin Web 框架完整教程
前端·golang·gin
Clown955 小时前
Gin从入门到精通(八)身份验证与授权(JWT)
gin
Golinie5 小时前
【Gin-Web】Bluebell社区项目梳理6:限流策略-漏桶与令牌桶
web·gin·令牌桶算法·限流策略
江禾藜7 小时前
rgin命令行工具--一键生成gin框架的开发脚手架
go·gin·命令行
Clown9520 小时前
Gin从入门到精通 (二)多种格式数据输出
gin
meowrain10 天前
Go语言 Web框架Gin
前端·golang·gin
Golinie10 天前
【Gin】Web框架开发快速入门
golang·web·gin·后端开发
莫忘初心丶12 天前
Golang Gin框架获取JSON输入
golang·json·gin
m0_7482482319 天前
Go-Gin Web 框架完整教程
前端·golang·gin