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