使用Echo框架构建REST API:无落地转发文件遇FormData解析错误
Echo转发文件到第三方API时"Could not parse content as FormData"问题解决
问题原因
你当前代码的核心问题是:直接把文件流作为POST请求体发送,虽然手动设置了Content-Type: multipart/form-data,但完全不符合multipart表单的规范——既没有添加边界分隔符(boundary),也没有把文件包装成指定的表单字段(比如下游API预期的"file"字段),导致对方无法解析表单数据。
修正方案
使用Go标准库的mime/multipart来构建符合规范的multipart请求体,自动处理boundary和表单结构:
修改后的完整代码
首先调整Handler的UploadFile方法,传递文件名给下游服务:
func (h *Handler) UploadFile(c echo.Context) error { formFile, err := c.FormFile("file") if err != nil { return err } file, err := formFile.Open() if err != nil { return err } defer file.Close() // 传递文件名给Upload方法,保证表单字段包含正确的文件名 cid, err := h.s.Upload(context.Background(), file, formFile.Filename) if err != nil { return err } return c.String(http.StatusOK, "Ok") }
然后重写Storage的Upload方法,用multipart.Writer构建合法的表单请求:
import ( "bytes" "io" "mime/multipart" "net/http" // 保留你原有的其他依赖 ) func (s *Storage) Upload(ctx context.Context, file io.Reader, fileName string) (cid.Cid, error) { // 创建缓冲区存储multipart表单数据 var buf bytes.Buffer writer := multipart.NewWriter(&buf) // 创建名为"file"的表单字段,传入文件名 formFile, err := writer.CreateFormFile("file", fileName) if err != nil { return cid.Undef, err } // 将文件内容复制到表单字段中 _, err = io.Copy(formFile, file) if err != nil { return cid.Undef, err } // 必须关闭writer,否则会缺少boundary结束标记 if err = writer.Close(); err != nil { return cid.Undef, err } // 创建POST请求,使用缓冲区作为请求体 req, err := http.NewRequest(http.MethodPost, s.config.endpoint+"/upload", &buf) if err != nil { return cid.Undef, err } // 设置请求头:Content-Type由writer自动生成(包含正确的boundary) req.Header.Set("accept", "application/json") req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.config.token)) res, err := s.config.hc.Do(req) if err != nil { return cid.Undef, err } defer res.Body.Close() // 及时关闭响应体,避免资源泄漏 d := json.NewDecoder(res.Body) if res.StatusCode != http.StatusOK { return cid.Undef, fmt.Errorf("unexpected response status: %d", res.StatusCode) } var out struct { Cid string `json:"cid"` } if err = d.Decode(&out); err != nil { return cid.Undef, err } return cid.Parse(out.Cid) }
关键注意点
multipart.Writer会自动生成唯一的boundary,并通过FormDataContentType()返回正确的Content-Type值(格式为multipart/form-data; boundary=xxxx),这是下游API能解析表单的核心。- 必须用
CreateFormFile创建对应字段的表单项,确保文件被包装在下游API预期的字段名下(这里是"file")。 - 一定要关闭
multipart.Writer,否则请求体末尾会缺少boundary的结束标记,依然会导致解析失败。 - 别忘了关闭响应体
res.Body,避免HTTP客户端的连接泄漏。
内容的提问来源于stack exchange,提问作者Danila Derzhavin
相关产品推荐
相关产品推荐

