{"record":{"id":"64e048fdc3f7e640","repo":"flipped-aurora/gin-vue-admin","slug":"function-file-open-failed-err-64e048","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/local.go","lineNumber":54,"sourceCode":"\t// 读取文件名并加密\n\tname := strings.TrimSuffix(file.Filename, ext)\n\tname = utils.MD5V([]byte(name))\n\t// 拼接新文件名\n\tfilename := name + \"_\" + time.Now().Format(\"20060102150405\") + ext\n\t// 尝试创建此路径\n\tmkdirErr := os.MkdirAll(global.GVA_CONFIG.Local.StorePath, os.ModePerm)\n\tif mkdirErr != nil {\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(mkdirErr).Error(\"function os.MkdirAll() failed\")\n\t\treturn \"\", \"\", errors.New(\"function os.MkdirAll() failed, err:\" + mkdirErr.Error())\n\t}\n\t// 拼接路径和文件名\n\tp := global.GVA_CONFIG.Local.StorePath + \"/\" + filename\n\tfilepath := global.GVA_CONFIG.Local.Path + \"/\" + filename\n\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\tout, createErr := os.Create(p)\n\tif createErr != nil {\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(createErr).Error(\"function os.Create() failed\")\n\n\t\treturn \"\", \"\", errors.New(\"function os.Create() failed, err:\" + createErr.Error())\n\t}\n\tdefer out.Close() // 创建文件 defer 关闭\n\n\t_, copyErr := io.Copy(out, f) // 传输（拷贝）文件\n\tif copyErr != nil {\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(copyErr).Error(\"function io.Copy() failed\")\n\t\treturn \"\", \"\", errors.New(\"function io.Copy() failed, err:\" + copyErr.Error())\n\t}\n\treturn filepath, filename, nil\n}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/upload/local.go#L36-L72","documentation":"local.UploadFile opens the multipart file handle with file.Open() before saving it to disk; an error there is wrapped in this message. It indicates the *multipart.FileHeader could not yield a readable file — usually because the multipart body is gone or malformed by the time Open is called.","triggerScenarios":"UploadFile(ctx, file, ...) where the FileHeader cannot be opened: request body already consumed/closed, truncated multipart payload, zero-byte aborted upload, or the header used after the HTTP request completed.","commonSituations":"Client aborts mid-upload, reverse proxy truncates large bodies, middleware (e.g. body-dumping logger) consumed the form, goroutine processing the FileHeader after the handler returned, server upload limits (MaxMultipartMemory / proxy client_max_body_size) cutting the payload.","solutions":["Open the file right in the handler before spawning goroutines or doing other body-consuming work","Raise proxy/client_max_body_size and Go's multipart limits to fit your max upload size","Return 400 early when Content-Length indicates truncation instead of attempting to open","Don't retain *multipart.FileHeader beyond the request lifetime; copy to a temp file if async processing is needed","Verify with curl -F that a small file uploads correctly to isolate size-related truncation"],"exampleFix":"// before\nfunc handler(c *gin.Context) {\n    fh, _ := c.FormFile(\"file\")\n    go func() { uploader.UploadFile(ctx, fh, name) }() // Open() fails: body closed\n}\n// after\nfunc handler(c *gin.Context) {\n    fh, _ := c.FormFile(\"file\")\n    f, _ := fh.Open()\n    defer f.Close()\n    go func() { save(f) }()\n}","handlingStrategy":"try-catch","validationCode":"fh, err := c.FormFile(\"file\")\nif err != nil {\n    return c.JSON(400, gin.H{\"msg\": \"no valid file in request\"})\n}\nif fh.Size == 0 {\n    return c.JSON(400, gin.H{\"msg\": \"empty upload rejected\"})\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 was truncated or already consumed\"})\n    }\n    if strings.Contains(err.Error(), \"os.Create() failed\") {\n        return c.JSON(500, gin.H{\"msg\": \"server storage write failed\"})\n    }\n    return err\n}","preventionTips":["Consume the multipart header once, synchronously in the handler","Align proxy body-size limits (e.g. nginx client_max_body_size) with app limits","Reject truncated/zero-length uploads with 400 before touching storage","For async processing, open the file first and pass the handle, not the FileHeader","Load-test large uploads through the full proxy chain to catch truncation early"],"tags":["upload","multipart","filesystem","local-storage"],"backgroundTag":"multipart-file-open-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}