单/多文件上传与大小限制实现

结论先行

单文件:c.FormFile("file") 拿文件头,c.SaveUploadedFile 存盘;多文件:c.MultipartForm() 拿到 form.File["files"] 后遍历。注意 engine.MaxMultipartMemory(默认 32MB)只是“内存转临时文件的阈值”,不是总大小上限;真正的硬限制要用 http.MaxBytesReader 包住请求体。

关键点

主题说明
取单文件c.FormFile("fieldName") → *multipart.FileHeader
存文件c.SaveUploadedFile(fh, dst),内部 open+copy
取多文件c.MultipartForm().File["files"] → []*FileHeader
内存阈值engine.MaxMultipartMemory 默认 32MB,超过的部分落临时文件
总大小限制c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, max),超限时报错(Go 1.19+ 可断言 *http.MaxBytesError)
文件名安全不要信任 file.Filename 直接拼路径:目录穿越/覆盖风险,用服务端生成文件名 + 白名单后缀

代码示例

func uploadSingle(c *gin.Context) {
    // 硬限制:整个 body ≤ 8MB
    c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 8<<20)

    fh, err := c.FormFile("file")
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    // 服务端重命名,绝不直接拼接 fh.Filename
    dst := filepath.Join("uploads", uuid.NewString()+filepath.Ext(fh.Filename))
    if err := c.SaveUploadedFile(fh, dst); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "save failed"})
        return
    }
    c.JSON(http.StatusOK, gin.H{"path": dst})
}

func uploadMany(c *gin.Context) {
    c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 32<<20)
    form, err := c.MultipartForm() // 需要先 ParseMultipartForm
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    for _, fh := range form.File["files"] { // 字段名 files,多选
        _ = c.SaveUploadedFile(fh, filepath.Join("uploads", fh.Filename))
    }
    c.JSON(http.StatusOK, gin.H{"count": len(form.File["files"])})
}

进阶注意

  • MaxBytesReader 限制的是整个 body:多个文件、多个字段合计超限都会失败,正好防总量攻击。
  • 超大文件(GB 级)别走 multipart 全量缓存:直接流式读 c.Request.Body 分段落盘。
  • 生产建议把上传目录放独立磁盘/对象存储,并做扩展名 + 内容嗅探白名单校验。

常见追问 / 记忆点

  • 追问:MaxMultipartMemory 调大是不是就能传大文件?答:不是——它只决定多少数据驻留内存、多少落临时文件,改它只是调“内存/磁盘”分界,不构成大小限制。
  • 追问:为什么不能直接用 fh.Filename 存盘?答:恶意文件名可含 ../ 造成目录穿越,或覆盖已有文件。
  • 记忆点:FormFile 单文件 / MultipartForm 多文件 / MaxBytesReader 限总量 / 文件名服务端生成。
笔记加载中…