如何在Go中流式读取请求Body并写入新请求Body?
如何在Golang中流式转发HTTP请求Body而不全部读入内存?
看你现在是把请求Body一次性全读进内存再转发,代码是这样的:
// read all bytes from body bytes, err := ioutil.ReadAll(request.Body) // set the bytes as NewReader to new request.body request, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(bytes))
这种做法在处理大文件上传或者大请求体的时候,很容易吃满内存,其实流式转发的实现非常直接,核心就是直接复用原始请求的Body(它本身就是io.Reader接口),不过有几个细节得留意,我给你拆解一下:
核心实现:直接流式转发
// 直接把原始请求的Body传给新请求,不用读进内存 newReq, err := http.NewRequest(http.MethodPut, targetURL, request.Body) if err != nil { // 出错时记得先关闭原始Body,避免资源泄漏 request.Body.Close() return err } // 复制原始请求的所有请求头,尤其是Content-Length这类和Body相关的头 newReq.Header = request.Header.Clone() // 发送请求 client := &http.Client{} resp, err := client.Do(newReq) if err != nil { // 请求发送失败的话,同样要手动关闭原始Body request.Body.Close() return err } // 后续处理响应...
特殊场景:需要保留原始请求Body的情况
如果你的业务逻辑中,在转发之后还需要读取原始请求的Body内容,那直接传原始Body就不行了(因为io.Reader是一次性的,读完就没了),这时候可以用io.TeeReader来同时实现流式转发和内容缓存:
// 创建一个buffer用来缓存原始Body内容 bodyBuf := &bytes.Buffer{} // TeeReader会在读取原始Body的同时,把内容写入bodyBuf teeReader := io.TeeReader(request.Body, bodyBuf) // 用teeReader作为新请求的Body,实现流式转发 newReq, err := http.NewRequest(http.MethodPut, targetURL, teeReader) if err != nil { request.Body.Close() return err } newReq.Header = request.Header.Clone() // 发送请求 client := &http.Client{} resp, err := client.Do(newReq) if err != nil { request.Body.Close() return err } // 现在bodyBuf里就保存了原始请求的Body内容,可以后续使用 originalBodyBytes := bodyBuf.Bytes() // 最后别忘了关闭原始Body request.Body.Close()
几个关键注意点
- 资源泄漏问题:不管请求成功还是失败,都要确保原始请求的Body被关闭,否则会占用连接资源
- 请求头复制:一定要复制原始请求的Header,特别是
Content-Length、Content-Type这些和Body相关的头,不然新请求可能无法正确解析Body - 分块编码兼容:如果原始请求没有设置
Content-Length,HTTP客户端会自动使用分块编码(Transfer-Encoding: chunked)来流式发送,不需要额外处理
内容的提问来源于stack exchange,提问作者Bluetear
相关产品推荐
相关产品推荐

