我有以下代码,假设通过将文件分成多个部分来下载文件。但是现在它只适用于图像,当我尝试下载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)
} 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的Range标头下载每个文件,下载后,它会寻找文件中写入该块的位置。
答案 0 :(得分:3)
如果您真的忽略了上面看到的许多错误,那么您的代码不应该对任何文件类型可靠地工作。
但是,我想我可以在你的代码中看到问题。我认为混合O_APPEND和seek可能是一个错误(在这种模式下应该忽略Seek)。我建议改用(*os.File).WriteAt。
IIRC,O_APPEND强制任何写入发生在文件的[当前]结尾。但是,文件部分的download_chunk
函数实例可能以不可预测的顺序执行,从而“重新排序”文件部分。结果是一个损坏的文件。
答案 1 :(得分:1)
1.执行程序的顺序不确定。 例如。执行结果可能如下:
...
文件长度:20902
范围10451-20901:10451
范围0-10450:10451
...
所以这些块不能只是追加。
2.写chunk数据时必须有sys.Mutex
(我的英语很差,请忘了)