使用Go发送multipart/form-data POST请求时遇500错误求助
问题分析与修复方案
核心问题
你的代码存在两个关键问题,导致Express服务无法解析请求体:
- Multipart表单未完成写入:
multipart.Writer需要显式调用Close()来写入表单的结束边界,否则请求体不完整,Express解析时会判定请求为空。原代码中defer writer.Close()会在函数执行完毕才触发,此时请求已经发送,buffer中没有完整的表单数据。 - 忽略字段写入错误:代码用
_忽略了writer.WriteField()的返回错误,若字段写入失败,会直接导致请求体损坏,无法被正确解析。
修复后的代码
func RenderAndUploadToS3(files []*multipart.FileHeader, browserName string, paths string, screenshotHeight int, screenshotWidth int, buildId string) error { var buffer bytes.Buffer writer := multipart.NewWriter(&buffer) numberOfFiles := len(files) logger.InfoLog("The number of stories to be rendered for buildId: " + buildId + " is " + strconv.Itoa(numberOfFiles)) for i := 0; i < numberOfFiles; i++ { f, err := files[i].Open() if err != nil { logger.ErrorLog("Failed to open file for buildId:" + buildId + " due to " + err.Error()) return err } defer f.Close() // 用defer确保文件一定会关闭 // 创建文件字段 formFile, err := writer.CreateFormFile("html", files[i].Filename) if err != nil { logger.ErrorLog("Failed to create writer for file for buildId: " + buildId + " due to " + err.Error()) return err } _, err = io.Copy(formFile, f) if err != nil { logger.ErrorLog("Error while copying file contents into writer for buildId: " + buildId + " due to " + err.Error()) return err } } // 检查所有字段写入的错误,不能忽略 if err := writer.WriteField("browserName", browserName); err != nil { logger.ErrorLog("Failed to write browserName field for buildId: " + buildId + " due to " + err.Error()) return err } if err := writer.WriteField("paths", paths); err != nil { logger.ErrorLog("Failed to write paths field for buildId: " + buildId + " due to " + err.Error()) return err } if err := writer.WriteField("height", strconv.Itoa(screenshotHeight)); err != nil { logger.ErrorLog("Failed to write height field for buildId: " + buildId + " due to " + err.Error()) return err } if err := writer.WriteField("width", strconv.Itoa(screenshotWidth)); err != nil { logger.ErrorLog("Failed to write width field for buildId: " + buildId + " due to " + err.Error()) return err } if err := writer.WriteField("buildId", buildId); err != nil { logger.ErrorLog("Failed to write buildId field for buildId: " + buildId + " due to " + err.Error()) return err } // 必须在创建请求前关闭writer,写入表单结束边界 if err := writer.Close(); err != nil { logger.ErrorLog("Failed to close multipart writer for buildId: " + buildId + " due to " + err.Error()) return err } url := constants.DOTUI_HOST + constants.DOTUI_RENDER_ROUTE_V2 logger.InfoLog("The url for sending dotui request is " + url) req, err := http.NewRequest("POST", url, &buffer) if err != nil { logger.ErrorLog("Failed to do POST request for buildId: " + buildId + " due to " + err.Error()) return err } req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} resp, err := client.Do(req) if err != nil { logger.ErrorLog("Failed to do POST request due to " + err.Error()) return err } defer resp.Body.Close() b, err := io.ReadAll(resp.Body) if err != nil { logger.ErrorLog("Failed to read POST response body from screenshot-render api v2 for buildId:" + buildId + "due to " + err.Error()) return err } logger.InfoLog("The response from screenshot-render api v2 for buildId: " + buildId + " is " + string(b)) return nil }
额外优化点
- 文件打开后用
defer f.Close()替代手动Close,避免中间出错return时文件未关闭。 - 所有字段写入操作都添加错误捕获,防止因写入失败导致请求体损坏。
内容的提问来源于stack exchange,提问作者arush saxena
相关产品推荐
相关产品推荐

