doumowu7371 2015-05-20 01:42
浏览 46
已采纳

io.copyN在第一次不被调用时无法工作

I'm trying to download a image from a site, and the steps are as follow:

  • use http.Get to fetch the image
  • use os.Create to create a new file in current folder
  • use io.copyN to copy the image into the file

But it is weird if the io.CopyN is failed at the first time, it seems never success later

code fragment:

    download_again:
        copy_byte, copy_err := io.CopyN(file, res.Body, res.ContentLength)
        fmt.Fprintf(os.Stderr, "img(%s) size: %d
", 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
", 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
", img_url, name)
                fout.WriteString(name + "
")
                return
            }
            fmt.Fprintf(os.Stderr, "error in download img(%s)[%f KB(%d B)]: %s, try %d times
", img_url, float64(copy_byte)/1024, copy_byte, name, try_i)
            try_i++
            goto download_again
        }

and the output message:

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

Any help will be appreciated :)

  • 写回答

1条回答 默认 最新

  • duanqian6982 2015-05-20 03:49
    关注

    You need to retry the http request. (not just the io.CopyN)

    Here is a (mostly untested) implementation of a resumable download:

    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
    }
    

    You can pass that function an offset and it will try to pick up from that point (either by telling the HTTP server, or by discarding the bytes). It also returns the progress you've made, so you can then wrap this function in a retry loop:

    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")
    }
    

    Incidentally, you should really avoid using goto for loops.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 乌班图ip地址配置及远程SSH
  • ¥15 怎么让点阵屏显示静态爱心,用keiluVision5写出让点阵屏显示静态爱心的代码,越快越好
  • ¥15 PSPICE制作一个加法器
  • ¥15 javaweb项目无法正常跳转
  • ¥15 VMBox虚拟机无法访问
  • ¥15 skd显示找不到头文件
  • ¥15 机器视觉中图片中长度与真实长度的关系
  • ¥15 fastreport table 怎么只让每页的最下面和最顶部有横线
  • ¥15 java 的protected权限 ,问题在注释里
  • ¥15 这个是哪里有问题啊?