【Gin】文件上传下载(单文件 + 多文件)

文件上传页面

🚀 一、单文件上传

核心逻辑

  1. 获取单个上传文件
  2. 校验文件是否存在
  3. 生成唯一文件名(纳秒时间戳)
  4. 保存文件到本地目录
  5. 返回标准化响应

Handler 代码

Go 复制代码
// SingleFileUpload 单文件上传
// 前端form表单key:file
func SingleFileUpload(c *gin.Context) {
	// 1. 获取单个文件
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 400,
			"msg":  "获取上传文件失败",
			"err":  err.Error(),
		})
		return
	}

	// 2. 生成唯一文件名,防止重名覆盖
	ext := filepath.Ext(file.Filename) // 获取文件后缀
	fileName := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
	savePath := "./upload/" + fileName // 保存路径

	// 3. 保存文件到服务器
	err = c.SaveUploadedFile(file, savePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  "文件保存失败",
			"err":  err.Error(),
		})
		return
	}

	// 4. 返回成功结果
	c.JSON(http.StatusOK, gin.H{
		"code":     200,
		"msg":      "单文件上传成功",
		"file_name": fileName,
		"file_size": file.Size,
	})
}

文件信息保存数据库

Go 复制代码
	fileInfo := model.File{}
	fileInfo.Name = file.Filename
	fileInfo.Size = fmt.Sprintf("%.2f MB", float64(file.Size)/1024/1024)
	fileInfo.Type = strings.TrimLeft(ext, ".")
	fileInfo.Time = time.Now().Format(time.DateOnly)
	fileInfo.Url = filename
	err = service.SaveFile(fileInfo)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 500,
			"msg":  "文件信息保存失败",
		})
		return
	}

📂 二、多文件上传

核心逻辑

  1. 解析多文件表单
  2. 获取文件列表并校验
  3. 遍历文件批量保存
  4. 统计上传结果
  5. 返回完整响应数据

Handler 代码

Go 复制代码
// MultiFileUpload 多文件上传
// 前端form表单key:file(支持多选)
func MultiFileUpload(c *gin.Context) {
	// 1. 解析multipart表单
	form, err := c.MultipartForm()
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 400,
			"msg":  "解析上传表单失败",
			"err":  err.Error(),
		})
		return
	}

	// 2. 获取所有上传文件
	files := form.File["file"]
	if len(files) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 400,
			"msg":  "未选择任何上传文件",
		})
		return
	}

	// 存储成功的文件名
	var successFiles []string

	// 3. 遍历保存所有文件
	for _, file := range files {
		ext := filepath.Ext(file.Filename)
		fileName := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
		savePath := "./upload/" + fileName

		// 保存单个文件
		if err := c.SaveUploadedFile(file, savePath); err == nil {
			successFiles = append(successFiles, fileName)
		}
	}

	// 4. 返回上传结果
	c.JSON(http.StatusOK, gin.H{
		"code":          200,
		"msg":           "多文件上传完成",
		"total_count":   len(files),       // 总文件数
		"success_count": len(successFiles), // 成功上传数
		"files":         successFiles,      // 成功文件名列表
	})
}

✅ 三、核心知识点总结

  1. 单文件c.FormFile("file") 获取文件 + c.SaveUploadedFile() 保存
  2. 多文件c.MultipartForm() 解析表单 + 遍历批量保存
  3. 唯一命名time.Now().UnixNano() 生成纳秒时间戳,彻底避免文件覆盖

四、文件下载

HTML关键代码

html 复制代码
<a href="/download/${file.url}" download >
    <i class="fas fa-download"></i>
</a>

Handler 代码

Go 复制代码
func GetDownloadFile(c *gin.Context) {
	url := c.Param("url")
	c.FileAttachment("C:/Users/87417/Desktop/2/"+url, url)
}
相关推荐
小邓的技术笔记3 天前
解析 gin 接收到 http 请求是怎么处理的
网络协议·http·gin
妙码生花6 天前
从 PHP 到 AI + Golang,程序员自救转型手记(三十三):权限规则管理器
后端·go·gin
北冥you鱼13 天前
Gin 架构在区块链应用中的最佳实践
架构·区块链·gin
赫媒派17 天前
Gin 12年零破坏API,架构哲学如何练成?
后端·go·gin
先跑起来再说1 个月前
Go 排行榜系统的工程化实现:分布式锁、快照表与定时刷新
分布式·go·gin
必胜刻1 个月前
一个异步生成游戏功能的落地复盘:Redis Stream + WebSocket + 状态补偿
redis·websocket·golang·gin·状态补偿
TE-茶叶蛋2 个月前
B-tree vs GIN Trigram vs HNSW
gin
曾几何时`2 个月前
Go(一)Gin框架 和 GORM机制
开发语言·golang·gin
老毛肚2 个月前
Gin + GORM 接口可视化测试
gin