{"record":{"id":"dfbf7122627ec96b","repo":"flipped-aurora/gin-vue-admin","slug":"function-file-open-failed-err-dfbf71","errorCode":null,"errorMessage":"function file.Open() failed, err:","messagePattern":"function file\\.Open\\(\\) failed, err:","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/upload/cloudflare_r2.go","lineNumber":36,"sourceCode":"\t\"github.com/flipped-aurora/gin-vue-admin/server/global\"\n\t\"github.com/flipped-aurora/gin-vue-admin/server/utils/logger\"\n)\n\ntype CloudflareR2 struct{}\n\nfunc (c *CloudflareR2) UploadFile(ctx context.Context, file *multipart.FileHeader) (fileUrl string, fileName string, err error) {\n\tclient, err := c.newR2Client()\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tuploader := manager.NewUploader(client)\n\n\tfileKey := fmt.Sprintf(\"%d_%s\", time.Now().Unix(), file.Filename)\n\tfileName = fmt.Sprintf(\"%s/%s\", global.GVA_CONFIG.CloudflareR2.Path, fileKey)\n\tf, openError := file.Open()\n\tif openError != nil {\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(openError).Error(\"function file.Open() failed\")\n\t\treturn \"\", \"\", errors.New(\"function file.Open() failed, err:\" + openError.Error())\n\t}\n\tdefer f.Close() // 创建文件 defer 关闭\n\n\t_, err = uploader.Upload(context.TODO(), &s3.PutObjectInput{\n\t\tBucket: aws.String(global.GVA_CONFIG.CloudflareR2.Bucket),\n\t\tKey:    aws.String(fileName),\n\t\tBody:   f,\n\t})\n\tif err != nil {\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(err).Error(\"function uploader.Upload() failed\")\n\t\treturn \"\", \"\", err\n\t}\n\n\treturn fmt.Sprintf(\"%s/%s\", global.GVA_CONFIG.CloudflareR2.BaseURL, fileName), fileKey, nil\n}\n\nfunc (c *CloudflareR2) DeleteFile(ctx context.Context, key string) error {\n\tclient, err := c.newR2Client()","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/upload/cloudflare_r2.go#L18-L54","documentation":"CloudflareR2.UploadFile calls file.Open() on the incoming multipart file handle before uploading to R2 via the S3-compatible uploader. If opening the multipart file part fails (e.g. the multipart section was already consumed or the request was malformed), this error wraps openError and aborts the upload.","triggerScenarios":"UploadFile(ctx, file, ...) where the *multipart.FileHeader cannot be opened: the underlying multipart form data was truncated, the file header was reused after the request body was closed, or the client sent a corrupt multipart payload.","commonSituations":"Uploading zero-byte/aborted requests, middleware already consumed the request body, request timeout closing the body before Open, proxies buffering truncation, reusing a FileHeader across goroutines after the request finished.","solutions":["Open the file (file.Open()) immediately after receiving the header, before other middleware or I/O touches the request body","Verify the HTTP client sends multipart/form-data with a proper Content-Length (avoid streaming without length through proxies that truncate)","Increase request/response timeouts if large uploads exceed server limits","Ensure the FileHeader is used only once and within the request's lifetime","Log the request size and Content-Type to confirm the multipart form was fully received"],"exampleFix":"// before\nfh := c.Request.MultipartForm.File[\"file\"][0]\ngo uploadLater(fh) // request body closed by then -> Open() fails\n// after\nf, err := fh.Open()\nif err != nil { ... }\ndefer f.Close()\ngo uploadLater(f)","handlingStrategy":"try-catch","validationCode":"fh, err := c.FormFile(\"file\")\nif err != nil {\n    return c.JSON(400, gin.H{\"msg\": \"multipart form missing or truncated\"})\n}\nif fh.Size == 0 {\n    return c.JSON(400, gin.H{\"msg\": \"empty file\"})\n}","typeGuard":"null","tryCatchPattern":"url, name, err := uploader.UploadFile(ctx, fileHeader, fileName)\nif err != nil {\n    if strings.Contains(err.Error(), \"file.Open() failed\") {\n        return c.JSON(400, gin.H{\"msg\": \"upload payload invalid or truncated\"})\n    }\n    return c.JSON(500, gin.H{\"msg\": \"upload failed\"})\n}","preventionTips":["Open the multipart file immediately after FormFile, before async work","Set proxy and server body-size limits consistently with client expectations","Reject empty/zero-size uploads early with 400","Never reuse *multipart.FileHeader after the request completes"],"tags":["upload","multipart","cloudflare-r2","storage"],"backgroundTag":"multipart-file-open-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}