简体   繁体   English

如何接收流式传输的HTTP响应

[英]How to receive HTTP Response for streaming

When throwing an HTTP Request with Go and receiving a Response, I want to receive a response while streaming, considering the case where the ResponseBody is huge (1 GB or more). 当使用Go抛出HTTP请求并接收响应时,我想在流式传输时收到响应,考虑到ResponseBody很大(1 GB或更多)的情况。

resp, err: = http.Client.Do(req)

In this case, if the body is huge, I can not read the Header and I do not know the state of Response. 在这种情况下,如果身体很大,我无法读取标题,我不知道响应的状态。 Is there any solution? 有什么解决方案吗?

(Edit: If you're unable to get the "Content-length" header from the response, it is possible that the web service you're hitting doesn't return that header. In such a case, there's no way to know the length of the response body without reading it completely. You can simulate that in the following example by removing the line that sets the Content-length header in the response.) (编辑:如果您无法从响应中获取“Content-length”标头,则您正在点击的Web服务可能不会返回该标头。在这种情况下,无法知道响应主体的长度没有完全读取。您可以通过删除在响应中设置Content-length标头的行来模拟以下示例中的内容。)

The standard Go net/http package handles large responses very well. 标准的Go net/http包可以很好地处理大型响应。 Here's a self contained example to demonstrate: 这是一个自包含的示例来演示:

// Start a mock HTTP server that returns 2GB of data in the response. Make a
// HTTP request to this server and print the amount of data read from the
// response.
package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "strings"
    "time"
)

const oneMB = 1024 * 1024
const oneGB = 1024 * oneMB
const responseSize = 2 * oneGB

const serverAddr = "localhost:9999"

func startServer() {
    // Mock HTTP server that always returns 2GB of data
    go http.ListenAndServe(serverAddr, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        w.Header().Set("Content-length", fmt.Sprintf("%d", responseSize))

        // 1MB buffer that'll be copied multiple times to the response
        buf := []byte(strings.Repeat("x", oneMB))

        for i := 0; i < responseSize/len(buf); i++ {
            if _, err := w.Write(buf); err != nil {
                log.Fatal("Failed to write to response. Error: ", err.Error())
            }
        }
    }))

    // Some grace period for the server to start
    time.Sleep(100 * time.Millisecond)
}

func main() {
    startServer()

    // HTTP client
    req, err := http.NewRequest("GET", "http://"+serverAddr, nil)
    if err != nil {
        log.Fatal("Error creating HTTP request: ", err.Error())
    }

    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal("Error making HTTP request: ", err.Error())
    }

    // Read the response header
    fmt.Println("Response: Content-length:", resp.Header.Get("Content-length"))

    bytesRead := 0
    buf := make([]byte, oneMB)

    // Read the response body
    for {
        n, err := resp.Body.Read(buf)
        bytesRead += n

        if err == io.EOF {
            break
        }

        if err != nil {
            log.Fatal("Error reading HTTP response: ", err.Error())
        }
    }

    fmt.Println("Response: Read", bytesRead, "bytes")
}

You wouldn't want to read the entire response in memory if it's too large. 如果它太大,你不会想要在内存中读取整个响应。 Write it to a temporary file instead and then process that. 将其写入临时文件,然后进行处理。

If instead you're looking for options to do this reliably when the network isn't very reliable, look for "HTTP range requests" using which you can resume partially downloaded data. 如果您正在寻找可靠的选项,以便在网络不可靠时可靠地执行此操作,请查找“HTTP范围请求”,您可以使用该“范围请求”恢复部分下载的数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM