普通网友 2013-08-23 06:20
浏览 20
已采纳

转到文件下载器

I have the following code which is suppose to download file by splitting it into multiple parts. But right now it only works on images, when I try downloading other files like tar files the output is an invalid file.

UPDATED:

Used os.WriteAt instead of os.Write and removed os.O_APPEND file mode.

package main

import (
    "errors"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "strconv"
)

var file_url string
var workers int
var filename string

func init() {
    flag.StringVar(&file_url, "url", "", "URL of the file to download")
    flag.StringVar(&filename, "filename", "", "Name of downloaded file")
    flag.IntVar(&workers, "workers", 2, "Number of download workers")
}

func get_headers(url string) (map[string]string, error) {
    headers := make(map[string]string)
    resp, err := http.Head(url)
    if err != nil {
        return headers, err
    }

    if resp.StatusCode != 200 {
        return headers, errors.New(resp.Status)
    }

    for key, val := range resp.Header {
        headers[key] = val[0]
    }
    return headers, err
}

func download_chunk(url string, out string, start int, stop int) {
    client := new(http.Client)
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", start, stop))
    resp, _ := client.Do(req)

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalln(err)
        return
    }

    file, err := os.OpenFile(out, os.O_WRONLY, 0600)
    if err != nil {
        if file, err = os.Create(out); err != nil {
            log.Fatalln(err)
            return
        }
    }
    defer file.Close()

    if _, err := file.WriteAt(body, int64(start)); err != nil {
        log.Fatalln(err)
        return
    }

    fmt.Println(fmt.Sprintf("Range %d-%d: %d", start, stop, resp.ContentLength))
}

func main() {
    flag.Parse()
    headers, err := get_headers(file_url)
    if err != nil {
        fmt.Println(err)
    } else {
        length, _ := strconv.Atoi(headers["Content-Length"])
        bytes_chunk := length / workers
        fmt.Println("file length: ", length)
        for i := 0; i < workers; i++ {
            start := i * bytes_chunk
            stop := start + (bytes_chunk - 1)
            go download_chunk(file_url, filename, start, stop)
        }
        var input string
        fmt.Scanln(&input)
    }
}

Basically, it just reads the length of the file, divides it with the number of workers then each file downloads using HTTP's Range header, after downloading it seeks to a position in the file where that chunk is written.

  • 写回答

2条回答 默认 最新

  • dqwh1201 2013-08-23 06:43
    关注

    If you really ignore many errors like seen above then your code is not supposed to work reliably for any file type.

    However, I guess I can see on problem in your code. I think that mixing O_APPEND and seek is probably a mistake (Seek should be ignored with this mode). I suggest to use (*os.File).WriteAt instead.

    IIRC, O_APPEND forces any write to happen at the [current] end of file. However, your download_chunk function instances for file parts can be executing in unpredictable order, thus "reordering" the file parts. The result is then a corrupted file.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 运筹学中在线排序的时间在线排序的在线LPT算法
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 lammps拉伸应力应变曲线分析
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥15 请问Lammps做复合材料拉伸模拟,应力应变曲线问题
  • ¥30 python代码,帮调试,帮帮忙吧