Http 下载带有范围请求的内容会导致损坏

Http 下载带有范围请求的内容会导致损坏,http,go,range-requests,Http,Go,Range Requests,我已经在Github上设置了一个基本项目:。 本质上,这个项目尝试使用HTTP范围请求下载一个文件,组装它,并将其保存回磁盘。我正在努力跟随(暂时不去参加戈罗廷的比赛)。当我尝试使用范围请求下载文件时,在合并所有请求数据后,最终大小大于我将获得的原始大小,并且最终文件已损坏 下面是负责下载该文件的代码 type Manager struct{ limit int } func NewManager(limit int) *Manager{ return &Manager

我已经在Github上设置了一个基本项目:。 本质上,这个项目尝试使用HTTP范围请求下载一个文件,组装它,并将其保存回磁盘。我正在努力跟随(暂时不去参加戈罗廷的比赛)。当我尝试使用范围请求下载文件时,在合并所有请求数据后,最终大小大于我将获得的原始大小,并且最终文件已损坏

下面是负责下载该文件的代码

type Manager struct{
    limit int
}

func NewManager(limit int) *Manager{
    return &Manager{
        limit: limit,
    }
}

func (m *Manager) DownloadBody(url string ) ([]byte ,error){
    // First we need to determine the filesize
    body := make([]byte ,0)
    response , err := http.Head(url) // We perform a Head request to get header information

    if response.StatusCode != http.StatusOK{
        return nil ,fmt.Errorf("received code %d",response.StatusCode)
    }
    if err != nil{
        return nil , err
    }

    maxConnections := m.limit // Number of maximum concurrent co routines
    bodySize , _ := strconv.Atoi(response.Header.Get("Content-Length"))
    bufferSize :=(bodySize) / (maxConnections)
    diff := bodySize % maxConnections
    read := 0
    for i:=0;i<maxConnections;i++{
        min := bufferSize * i
        max := bufferSize * (i+1)
        if i==maxConnections-1{
            max+=diff // Check to see if we have any leftover data to retrieve for the last request
        }
        req , _ := http.NewRequest("GET" , url, nil)
        req.Header.Add("Range" ,fmt.Sprintf("bytes=%d-%d",min,max))
        res , e := http.DefaultClient.Do(req)
        if e != nil{
            return body , e
        }
        log.Printf("Index:%d . Range:bytes=%d-%d",i,min,max)
        data , e :=ioutil.ReadAll(res.Body)
        res.Body.Close()
        if e != nil{
            return body,e
        }
        log.Println("Data for  request: ",len(data))
        read = read + len(data)
        body = append(body, data...)
    }
    log.Println("File size:",bodySize , "Downloaded size:",len(body)," Actual read:",read)
    return body, nil
}
注意:目前我对并发代码不感兴趣

你知道我会错过什么吗

注意:我已使用下面的curl命令确认服务器返回206部分内容:


curl-Ihttps://media.wired.com/photos/5a593a7ff11e325008172bc2/16:9/w_2400,h_1350,c_limit/pulsar-831502910.jpg

多亏了@mh cbon,我写了一个简单的测试,帮助我找到了解决方案。这是固定密码

for i:=0;i<maxConnections;i++{
        min := bufferSize * i
        if i != 0{
            min++
        }
        max := bufferSize * (i+1)
        if i==maxConnections-1{
            max+=diff // Check to see if we have any leftover data to retrieve for the last request
        }
        req , _ := http.NewRequest("GET" , url, nil)
        req.Header.Add("Range" ,fmt.Sprintf("bytes=%d-%d",min,max))
        res , e := http.DefaultClient.Do(req)
        if e != nil{
            return body , e
        }
        log.Printf("Index:%d . Range:bytes=%d-%d",i,min,max)
        data , e :=ioutil.ReadAll(res.Body)
        res.Body.Close()
        if e != nil{
            return body,e
        }
        log.Println("Data for  request: ",len(data))
        read = read + len(data)
        body = append(body, data...)
    }

当然还有更多的测试需要编写,但这是一个良好的开端

检查服务器是否实际返回了带有
内容范围
标题的206响应,而不是普通的200响应。服务器不需要满足范围请求。服务器确实会返回一个206。在您的情况下,最好的做法是邀请您编写测试,而不是提供解决方案。请看这篇文章,了解如何在go See中为范围请求提供服务。另外,如果您在编写测试时遇到困难,它可能看起来像@mh cbon感谢您提供这些链接。当我明天有空的时候,我会试着读更多的东西。你可以重写laast语句,如
(如果需要):=“你好,世界!!!!”;字符串(数据)=需要{t.Errorf(“需要%s.received:[%s]”,需要,数据)
是的,你是对的。我只是想快速编写一个测试来发现错误。我现在已经编写了更多的测试,很快就会提交并将它们推送到我的github项目。当你准备好后,你可能会想在stackreview上发布它更适合代码审阅。我已经很久没有使用代码审阅了。不过我可能会尝试一下d为0-100、101-200、201-300等。您可以执行0-99、100-199、200-299等,不需要任何特殊情况:)
for i:=0;i<maxConnections;i++{
        min := bufferSize * i
        if i != 0{
            min++
        }
        max := bufferSize * (i+1)
        if i==maxConnections-1{
            max+=diff // Check to see if we have any leftover data to retrieve for the last request
        }
        req , _ := http.NewRequest("GET" , url, nil)
        req.Header.Add("Range" ,fmt.Sprintf("bytes=%d-%d",min,max))
        res , e := http.DefaultClient.Do(req)
        if e != nil{
            return body , e
        }
        log.Printf("Index:%d . Range:bytes=%d-%d",i,min,max)
        data , e :=ioutil.ReadAll(res.Body)
        res.Body.Close()
        if e != nil{
            return body,e
        }
        log.Println("Data for  request: ",len(data))
        read = read + len(data)
        body = append(body, data...)
    }
func TestManager_DownloadBody(t *testing.T) {
    ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {

        http.ServeContent(writer,request ,"hey" ,time.Now() ,bytes.NewReader([]byte(`hello world!!!!`)))
    }))

    defer ts.Close()


    m := NewManager(4)
    data , err := m.DownloadBody(ts.URL)
    if err != nil{
        t.Errorf("%s",err)
    }

    if string(data) != "hello world!!!!"{
        t.Errorf("Expected hello world!!!! . received : [%s]",data)
    }
}