You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言Docker容器内文件上传首次失败二次成功问题求助

问题分析与解决方案

核心错误原因

你的代码存在逻辑错误:在通过c.BindJSON(&body)解析完请求体后,又尝试从c.Request.Body读取数据上传,但此时请求体已经被BindJSON读取完毕,导致上传的是空内容,这就是首次上传失败的根本原因。

本地Docker或直接服务器运行时看似正常,大概率是因为环境差异(比如部分环境隐式支持请求体重复读取),但这属于偶然情况,并非代码本身正确。

修正后的代码

将上传逻辑改为使用Base64解码后的decoded数据,而非已被读取的请求体:

func UploadFile() gin.HandlerFunc {
    return func(c *gin.Context) {
        var body settings.BodyContent

        if err := c.BindJSON(&body); err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"message": "Internal error", "success": false})
            return
        }

        // 建议全局初始化Minio客户端,避免每次请求创建
        cdn, err := minio.New("cdn.hahu.jobs", &minio.Options{
            Creds:  credentials.NewStaticV4(settings.MINIO_ACCESS_KEY, settings.MINIO_SECRET_KEY, ""),
            Secure: true,
        })
        if err != nil {
            log.Println("Auth error :: ", err)
            c.JSON(http.StatusInternalServerError, gin.H{
                "message": err.Error(),
                "success": false,
            })
            return
        }

        // 用更安全的方式处理路径后缀
        if !strings.HasSuffix(body.Input.Folder, "/") {
            body.Input.Folder += "/"
        }
        body.Input.Folder = path.Join("bridges", body.Input.Folder)

        urls := []string{}
        secure_urls := []string{}

        for i := range body.Input.Files {
            file := body.Input.Files[i]
            parts := strings.Split(file, ",")
            // 校验Base64格式合法性
            if len(parts) < 2 {
                c.JSON(http.StatusBadRequest, gin.H{
                    "message": "Invalid Base64 format",
                    "success": false,
                })
                return
            }
            fileBase64 := parts[1]

            decoded, err := base64.StdEncoding.DecodeString(fileBase64)
            if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{
                    "message": err.Error(),
                    "success": false,
                })
                return
            }

            kind, err := filetype.Match(decoded)
            if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{
                    "message": err.Error(),
                    "success": false,
                })
                return
            }

            id := path.Join(body.Input.Folder, randstr.Hex(50))

            ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
            defer cancel()

            // 使用Base64解码后的真实数据上传
            _, err = cdn.PutObject(ctx, "public", id, bytes.NewReader(decoded), int64(len(decoded)), minio.PutObjectOptions{
                ContentType: kind.MIME.Value,
            })
            if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{
                    "message": err.Error(),
                    "success": false,
                })
                return
            }

            url := fmt.Sprintf("http://cdn.hahu.jobs/public/%s", id)
            secure_url := fmt.Sprintf("https://cdn.hahu.jobs/public/%s", id)
            urls = append(urls, url)
            secure_urls = append(secure_urls, secure_url)

            // 只处理一个文件
            break
        }

        c.JSON(http.StatusOK, gin.H{
            "urls":        urls,
            "secure_urls": secure_urls,
        })
    }
}

额外优化建议

  1. 全局初始化Minio客户端:每次请求创建客户端会消耗资源,建议在程序启动时初始化一次,全局复用。
  2. 路径处理用path.Join:避免手动拼接路径出现的分隔符不一致问题。
  3. Base64格式校验:增加对分割结果长度的判断,避免非法格式导致程序崩溃。
  4. 明确文件大小:上传时传入int64(len(decoded))而非-1,让Minio更高效处理请求。

内容的提问来源于stack exchange,提问作者Solomon Sitotaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 02:29:57