猿问

去文件下载器

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


更新:


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


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)

    } 


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


肥皂起泡泡
浏览 209回答 2
2回答

呼唤远方

如果你真的忽略了上面看到的许多错误,那么你的代码不应该对任何文件类型可靠地工作。但是,我想我可以在您的代码中看到问题。我认为混合 O_APPEND 和 seek 可能是一个错误(这种模式应该忽略 Seek)。我建议使用(*os.File).WriteAt代替。IIRC, O_APPEND 强制任何写入发生在 [当前] 文件末尾。但是,download_chunk文件部分的函数实例可能会以不可预测的顺序执行,从而“重新排序”文件部分。结果是一个损坏的文件。

慕容3067478

1.goroutine的顺序不确定。 eg. 执行结果可能如下:...文件长度:20902范围 10451-20901:10451范围 0-10450:10451...所以块不能只是附加。2.当写入chunk数据时必须有一个sys.Mutex(我的英语很差,请忘记)
随时随地看视频慕课网APP

相关分类

Go
我要回答