You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断流式HTTP响应中byte数组rb的完成时机以进行JSON解码?

问题分析与解决方案

你忽略的核心关键点是:当ReadBytes返回io.EOF时,就代表整个分块HTTP响应已经读取完毕,此时rb就是完整的响应数据,可直接进行JSON解码。

Go的http包会自动处理分块传输编码的解码,你从resp.Body读到的就是原始的响应内容,不需要手动处理分块的边界标识,io.EOF就是整个响应结束的标志。

以下分两种常见场景给出修改后的代码:

场景1:服务端返回单个完整JSON(分块传输)

如果服务端返回的是一个完整的JSON结构,只是通过分块传输,那么在循环结束后即可解码rb:

package main

import (
    "bufio"
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

// 根据服务端实际返回的JSON结构定义结构体
type ResponseData struct {
    Message string `json:"message"`
    Data    any    `json:"data"`
}

func main() {
    body := []byte("test")

    resp, err := http.Post("http://localhost:8281/tap", "application/json", bytes.NewReader(body))
    if err != nil {
        fmt.Printf("%v\n", err)
        return
    }
    defer resp.Body.Close()
    fmt.Printf("Status: [%s]\n", resp.Status)
    fmt.Println()

    reader := bufio.NewReader(resp.Body)
    var rb []byte

    for {
        line, err := reader.ReadBytes('\n')
        if err != nil {
            if err == io.EOF {
                // 读取到最后一块数据,追加剩余内容后退出循环
                rb = append(rb, line...)
                break
            }
            fmt.Printf("Error reading streamed bytes: %v\n", err)
            return
        }
        rb = append(rb, line...)
    }

    // 循环结束,rb已包含完整响应,开始JSON解码
    var result ResponseData
    if err := json.Unmarshal(rb, &result); err != nil {
        fmt.Printf("JSON decode error: %v\n", err)
        return
    }
    fmt.Printf("Decoded result: %+v\n", result)
}

场景2:服务端返回JSON流(每行一个独立JSON对象)

如果服务端是每行返回一个独立的JSON对象(JSON Lines格式),则无需等待全部读取,每行读取后即可解码:

package main

import (
    "bufio"
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

// 每行JSON对应的结构体
type LineData struct {
    ID   int    `json:"id"`
    Text string `json:"text"`
}

func main() {
    body := []byte("test")

    resp, err := http.Post("http://localhost:8281/tap", "application/json", bytes.NewReader(body))
    if err != nil {
        fmt.Printf("%v\n", err)
        return
    }
    defer resp.Body.Close()
    fmt.Printf("Status: [%s]\n", resp.Status)
    fmt.Println()

    scanner := bufio.NewScanner(resp.Body)
    for scanner.Scan() {
        line := scanner.Bytes()
        var data LineData
        if err := json.Unmarshal(line, &data); err != nil {
            fmt.Printf("Decode line error: %v\n", err)
            continue
        }
        fmt.Printf("Decoded line: %+v\n", data)
    }

    if err := scanner.Err(); err != nil {
        fmt.Printf("Scanner error: %v\n", err)
    }
}

额外简化方案

如果响应数据量不大,可直接用io.ReadAll一次性读取整个响应体,无需循环:

rb, err := io.ReadAll(resp.Body)
if err != nil {
    fmt.Printf("Read error: %v\n", err)
    return
}
// 后续解码逻辑同上

内容的提问来源于stack exchange,提问作者user1513388

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:37:41