Go文件下载程序
我有下面的代码,假设通过将文件拆分为多个部分来下载该文件。但现在它只对图像有效,当我尝试下载其他文件(如tar文件)时,输出是无效的文件 更新: 使用了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
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))”无效。但是本地测试一切正常是的,它依赖于存在的内容长度
头以及为支持范围
头的文件提供服务的服务器。没有这些,就不会有效果。