【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)
}
相关推荐
福兮说5 天前
Gin 项目里的错误处理:让 Controller 不必知道错误是怎么来的
后端·go·gin·架构设计
妙码生花9 天前
两个月 59 篇 AI 开发日志 + Golang 商业级实战项目收工后,得来的 AI 使用心法-上
前端·后端·gin
王的宝库10 天前
Gin + GORM
开发语言·golang·gin
妙码生花10 天前
golang 应用服务端部署(使用 systemd 服务)
开发语言·人工智能·后端·golang·node.js·php·gin
JavaPub-rodert13 天前
后台操作日志千万别同步写库:从 ShiyuAdmin 看 Gin 异步审计日志设计
gin
王的宝库13 天前
Gin 框架实战
golang·gin
Vespeng18 天前
打破传统 MVC:在 Go 中实践高内聚的业务驱动架构
架构·go·gin