Go io.copyN在第一次未调用时无法工作

Go io.copyN在第一次未调用时无法工作,go,download,io,Go,Download,Io,我正在尝试从站点下载图像,步骤如下: 使用http.Get获取图像 使用os.Create在当前文件夹中创建新文件 使用io.copyN将图像复制到文件中 但是如果io.CopyN第一次失败,那就很奇怪了,以后似乎永远不会成功 代码片段: download_again: copy_byte, copy_err := io.CopyN(file, res.Body, res.ContentLength) fmt.Fprintf(os.Stderr, "i

我正在尝试从站点下载图像,步骤如下:

  • 使用
    http.Get
    获取图像
  • 使用
    os.Create
    在当前文件夹中创建新文件
  • 使用
    io.copyN
    将图像复制到文件中
但是如果
io.CopyN
第一次失败,那就很奇怪了,以后似乎永远不会成功

代码片段:

    download_again:
        copy_byte, copy_err := io.CopyN(file, res.Body, res.ContentLength)
        fmt.Fprintf(os.Stderr, "img(%s) size: %d\n", name, res.ContentLength)
        if copy_err == nil && res.ContentLength == copy_byte {
            fmt.Fprintf(os.Stderr, "Success to download img(%s)[%f KB(%d B)]: %s\n", img_url, float64(copy_byte)/1024, copy_byte, name)
        } else {
            if try_i > download_times {
                fmt.Fprintf(os.Stderr, "[fatal] fail to download img(%s) %s\n", img_url, name)
                fout.WriteString(name + "\n")
                return
            }
            fmt.Fprintf(os.Stderr, "error in download img(%s)[%f KB(%d B)]: %s, try %d times\n", img_url, float64(copy_byte)/1024, copy_byte, name, try_i)
            try_i++
            goto download_again
        }
以及输出消息:

img(11085) size: 273047
error in download img(./style/images/dszp/11085.jpg)[171.447266 KB(175562 B)]: 11085 , try 1 times
img(11085) size: 273047
error in download img(./style/images/dszp/11085.jpg)[0.000000 KB(0 B)]: 11085 , try 2 times
img(11085) size: 273047
error in download img(./style/images/dszp/11085.jpg)[0.000000 KB(0 B)]: 11085, try 3 times
img(11085) size: 273047
error in download img(./style/images/dszp/11085.jpg)[0.000000 KB(0 B)]: 11085, try 4 times
img(11085) size: 273047
error in download img(./style/images/dszp/11085.jpg)[0.000000 KB(0 B)]: 11085, try 5 times
img(11085) size: 273047
error in download img(./style/images/dszp/11085.jpg)[0.000000 KB(0 B)]: 11085, try 6 times

任何帮助都将不胜感激:)

您需要重试http请求。(不仅仅是
io.CopyN

以下是一个可恢复下载的(大部分未经测试)实现:

func downloadFile(dst *os.File, url string, offset int64) (int64, error) {
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return 0, err
    }
    // try to use a range header
    if offset > 0 {
        req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset))
    }
    res, err := http.DefaultClient.Do(req)
    if err != nil {
        return 0, err
    }
    defer res.Body.Close()

    // some http servers don't support range, so in that case we discard the first
    // offset bytes
    if offset > 0 && res.StatusCode != http.StatusPartialContent {
        _, err = io.CopyN(ioutil.Discard, res.Body, offset)
        if err != nil {
            return offset, err
        }
    }

    n, err := io.CopyN(dst, res.Body, res.ContentLength)
    return offset + n, err
}
您可以向该函数传递一个偏移量,它将尝试从该点开始拾取(通过通知HTTP服务器或丢弃字节)。它还返回您所取得的进展,因此您可以将此函数包装在重试循环中:

func example() error {
    f, err := os.Create("/tmp/pkg.png")
    if err != nil {
        return err
    }
    defer f.Close()

    offset := int64(0)
    delay := time.Second
    // try 5 times
    for i := 0; i < 5; i++ {
        offset, err = downloadFile(f, "http://golang.org/doc/gopher/pkg.png", offset)
        if err == nil {
            return nil
        }
        // wait a little while before trying again
        time.Sleep(delay)
        delay *= 2
    }
    return fmt.Errorf("failed to download file after 5 tries")
}
func example()错误{
f、 err:=os.Create(“/tmp/pkg.png”)
如果错误!=零{
返回错误
}
延迟f.关闭()
偏移量:=int64(0)
延迟:=时间。秒
//试5次
对于i:=0;i<5;i++{
偏移量,err=下载文件(f,“http://golang.org/doc/gopher/pkg.png“,偏移量)
如果err==nil{
归零
}
//请稍等片刻,然后重试
时间。睡眠(延迟)
延迟*=2
}
返回fmt.Errorf(“尝试5次后下载文件失败”)
}

顺便说一句,您应该避免对循环使用
goto

res.Body
是一个流,因此一旦您从其中读取了一次字节,就不能再读取它们。您必须重新连接到重新下载。调试代码可能会打印错误。如果你真的想把
goto
改成其他东西,可能
for trytes:=5;尝试>0;成功后尝试--{…}
中断
循环。(最后,要小心。)