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 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥15 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化