Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go文件下载程序_Go - Fatal编程技术网

Go文件下载程序

Go文件下载程序,go,Go,我有下面的代码,假设通过将文件拆分为多个部分来下载该文件。但现在它只对图像有效,当我尝试下载其他文件(如tar文件)时,输出是无效的文件 更新: 使用了os.WriteAt而不是os.Write并删除了os.O\u APPEND文件模式 package main import ( "errors" "flag" "fmt" "io/ioutil" "log" "net/http" "os" "strconv" ) var fil

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

更新:

使用了
os.WriteAt
而不是
os.Write
并删除了
os.O\u 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)
    } 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)
    }
}
主程序包
进口(
“错误”
“旗帜”
“fmt”
“io/ioutil”
“日志”
“net/http”
“操作系统”
“strconv”
)
var文件\u url字符串
var工人国际
变量文件名字符串
func init(){
flag.StringVar(&file\u url,“url”,“要下载的文件的url”)
flag.StringVar(&filename,“filename”,“下载文件的名称”)
flag.IntVar(&workers,“workers”,2,“下载worker的数量”)
}
func get_头(url字符串)(映射[string]字符串,错误){
标题:=make(映射[字符串]字符串)
resp,err:=http.Head(url)
如果错误!=零{
返回标题,错误
}
如果响应状态代码!=200{
返回标题,错误。新建(各自状态)
}
对于键,val:=范围和标题{
标题[key]=val[0]
}
返回标题,错误
}
func下载块(url字符串、输出字符串、起始整型、终止整型){
客户端:=新建(http.client)
req,u3;:=http.NewRequest(“GET”,url,nil)
请求头添加(“范围”,fmt.Sprintf(“字节=%d-%d”,开始,停止))
resp,u:=client.Do(请求)
延迟响应主体关闭()
body,err:=ioutil.ReadAll(resp.body)
如果错误!=零{
log.Fatalln(错误)
返回
}
file,err:=os.OpenFile(out,os.O_WRONLY,0600)
如果错误!=零{
如果是文件,err=os.Create(out);err!=nil{
log.Fatalln(错误)
返回
}
}
延迟文件。关闭()
如果3;,err:=file.WriteAt(body,int64(start));err!=nil{
log.Fatalln(错误)
返回
}
fmt.Println(fmt.Sprintf(“范围%d-%d:%d”,开始、停止,分别为ContentLength))
}
func main(){
flag.Parse()
头,错误:=获取头(文件url)
如果错误!=零{
fmt.Println(错误)
}否则{
长度,u:=strconv.Atoi(标题[“内容长度”])
字节\u块:=长度/工作者
fmt.Println(“文件长度:”,长度)
对于i:=0;i

基本上,它只是读取文件的长度,将其与工作进程数相除,然后使用HTTP的范围标头下载每个文件,下载后,它将查找文件中写入该区块的位置。

如果您真的忽略了上面看到的许多错误,那么您的代码对于任何文件类型都不可靠

然而,我想我可以看到你们代码中的问题。我认为混合O_APPEND和seek可能是一个错误(在这种模式下seek应该被忽略)。我建议改用


IIRC,O_APPEND强制在文件的[当前]结尾进行任何写入。但是,文件部分的
download\u chunk
函数实例可能会以不可预测的顺序执行,从而“重新排序”文件部分。结果就是一个损坏的文件。

如果您真的忽略了上面看到的许多错误,那么您的代码对于任何文件类型都不可靠

然而,我想我可以看到你们代码中的问题。我认为混合O_APPEND和seek可能是一个错误(在这种模式下seek应该被忽略)。我建议改用


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

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

档案长度:20902

范围10451-20901:10451

范围0-10450:10451

所以块不能只是附加

2.写入数据块时,数据块必须具有sys.Mutex


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

1.围棋程序的顺序不确定 执行结果可能如下:

档案长度:20902

范围10451-20901:10451

范围0-10450:10451

所以块不能只是附加

2.写入数据块时,数据块必须具有sys.Mutex


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

但我使用的是seek,所以不管哪个工人先完成都不重要。@Marconi,我在本地(win7+wamp)和远程(github.com上的zip文件)中测试了它。本地测试,zip下载是正确的,但是远程测试是错误的。在远程测试中,resp.ContentLength始终等于标头[“Content Length”],代码“req.Header.Add(“Range”、fmt.Sprintf(“bytes=%d-%d”、start、stop))”无效。但是本地测试一切正常是的,它依赖于存在的
内容长度
头以及为支持
范围
头的文件提供服务的服务器。如果没有这些,将无法工作。但我正在使用seek,所以不管哪个工人先完成。@Marconi,我已经在本地(win7+wamp)和远程(github.com上的zip文件)中对其进行了测试。本地测试,zip下载是正确的,但是远程测试是错误的。在远程测试中,resp.ContentLength始终等于标头[“Content Length”],代码“req.Header.Add(“Range”、fmt.Sprintf(“bytes=%d-%d”、start、stop))”无效。但是本地测试一切正常是的,它依赖于存在的
内容长度
头以及为支持
范围
头的文件提供服务的服务器。没有这些,就不会有效果。