{"record":{"id":"cbd9f6e3e40b0259","repo":"flipped-aurora/gin-vue-admin","slug":"fileheader","errorCode":null,"errorMessage":"构造 FileHeader 失败","messagePattern":"构造 FileHeader 失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/upload/chunk.go","lineNumber":121,"sourceCode":"\tpart, err := mw.CreateFormFile(fieldName, fileName)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif _, err = io.Copy(part, src); err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif err = mw.Close(); err != nil {\n\t\treturn nil, nil, err\n\t}\n\treader := multipart.NewReader(body, mw.Boundary())\n\tform, err := reader.ReadForm(1 << 20) // 1MB 以上落临时盘,避免大文件 OOM\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tfiles := form.File[fieldName]\n\tif len(files) == 0 {\n\t\t_ = form.RemoveAll()\n\t\treturn nil, nil, fmt.Errorf(\"构造 FileHeader 失败\")\n\t}\n\treturn files[0], func() { _ = form.RemoveAll() }, nil\n}\n","sourceCodeStart":103,"sourceCodeEnd":125,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/upload/chunk.go#L103-L125","documentation":"BuildFileHeader reconstructs a *multipart.FileHeader from raw chunk bytes by building an in-memory multipart form. After parsing the form, it expects at least one file under fieldName; if the form has none, it removes temp files and returns the error \"构造 FileHeader 失败\". This means the constructed multipart payload did not yield a file part under the expected field name.","triggerScenarios":"Calling BuildFileHeader with a fieldName that does not match the field name used when writing the multipart body, or with empty/invalid multipart content, so form.File[fieldName] is empty after parsing.","commonSituations":"Field name mismatch between chunk writer and reader (e.g. \"file\" vs \"chunk\"); mime/multipart form written without the file part; empty chunk data producing no part; incorrect boundary/Content-Type passed to the form parser.","solutions":["Ensure the fieldName argument exactly matches the form field name used to write the multipart part.","Verify the multipart body actually contains a file part with data before calling BuildFileHeader.","Check that the boundary and Content-Type used to parse the form are the ones used to write it.","Log len(form.File) keys after parsing to confirm which fields exist."],"exampleFix":"// before\nfh, _, err := BuildFileHeader(buf, \"chunk\") // written as field \"file\"\n\n// after\nfh, cleanup, err := BuildFileHeader(buf, \"file\") // matches the writer's field name","handlingStrategy":"validation","validationCode":"func hasFilePart(body io.Reader, boundary, fieldName string) bool {\n    r := multipart.NewReader(body, boundary)\n    form, err := r.ReadForm(32 << 20)\n    if err != nil { return false }\n    defer form.RemoveAll()\n    return len(form.File[fieldName]) > 0\n}","typeGuard":null,"tryCatchPattern":"fh, cleanup, err := upload.BuildFileHeader(buf, fieldName)\nif err != nil {\n    defer func() { if cleanup != nil { cleanup() } }()\n    return fmt.Errorf(\"no file part %q in constructed form: %w\", fieldName, err)\n}\ndefer cleanup()","preventionTips":["Define the multipart field name as a shared constant between writer and reader","Verify chunk bytes are non-empty before building the header","Reuse the same boundary/Content-Type when writing and parsing","Log form.File keys when debugging reconstruction failures"],"tags":["upload","multipart","file-io"],"backgroundTag":"multipart-file-part-missing","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}