首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Go文件下载器

Go文件下载器
EN

Stack Overflow用户
提问于 2013-08-23 06:20:38
回答 2查看 4.2K关注 0票数 1

我有下面的代码,它应该通过将文件分成多个部分来下载。但现在它只适用于图像,当我尝试下载其他文件(如tar文件)时,输出是无效文件。

更新:

使用os.WriteAt代替os.Write并删除os.O_APPEND文件模式。

代码语言:javascript
复制
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)
    }
}

基本上,它只是读取文件的长度,并将其除以工作人员的数量,然后使用HTTP的范围头下载每个文件,然后下载到写入该块的文件中的某个位置。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-08-23 06:43:31

如果您真的忽略了上面看到的许多错误,那么您的代码就不应该对任何文件类型可靠地工作。

但是,我想我可以在您的代码中看到问题。我认为混合O_APPEND和寻求可能是一个错误(在这种模式下寻求应该被忽略)。我建议用( os.File).WriteAt代替。

O_APPEND强制在当前文件的末尾进行任何写入。但是,文件部件的download_chunk函数实例可以以不可预测的顺序执行,从而“重新排序”文件部分。结果是一个损坏的文件。

票数 3
EN

Stack Overflow用户

发布于 2013-08-23 07:01:27

1. go例程的顺序不确定。例如:执行结果可能如下:

..。

档案长度:20902

范围10451-20901:10451

范围0-10450:10451

..。

所以这些块不能只是附加。

2.当写入块数据时必须有一个sys.Mutex

(我的英语很差,请忘了吧)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18396079

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档