在 GO 中重组大块 zip 下载
Recombining large chunked zip download in GO
我正在与 Accept-Ranges 和 Goroutines 并行下载一个大的 .zip 文件。该应用程序使用其范围 header.
从 URL 发送多个请求来下载 10MB 的 zip 文件块
请求被分成不同的范围作为单独的 Goroutines,并将获得的数据写入临时文件。文件名为 1、2、3....
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"sync"
)
var wg sync.WaitGroup
func main() {
url := "https://path/to/large/zip/file/zipfile.zip"
res, _ := http.Head(url)
maps := res.Header
length, _ := strconv.Atoi(maps["Content-Length"][0]) // Get the content length from the header request
chunks := (length / (1024 * 1024 * 10)) + 1
// startByte and endByte determines the positions of the chunk that should be downloaded
var startByte = 0
var endByte = (1024 * 1024 * 10) - 1
//body := make([][]byte, chunks)
body := make([]io.ReadCloser, chunks)
for i := 0; i < chunks; i++ {
wg.Add(1)
go func(min int, max int, i int) {
client := &http.Client {}
req, _ := http.NewRequest("GET", url, nil)
rangeHeader := "bytes=" + strconv.Itoa(min) +"-" + strconv.Itoa(max)
fmt.Println(rangeHeader)
req.Header.Add("Range", rangeHeader)
resp,_ := client.Do(req)
defer resp.Body.Close()
reader, _ := ioutil.ReadAll(resp.Body)
body[i] = resp.Body
ioutil.WriteFile(strconv.Itoa(i), reader, 777) // Write to the file i as a byte array
wg.Done()
}(startByte, endByte, i)
startByte = endByte + 1
endByte += 1024 * 1024 * 10
}
wg.Wait()
filepath := "zipfile.zip"
// Create the file
_, err := os.Create(filepath)
if err != nil {
return
}
file, _ := os.OpenFile(filepath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
log.Fatal(err)
}
for j := 0; j < chunks; j++ {
newFileChunk, err := os.Open(strconv.Itoa(j))
if err != nil {
log.Fatal(err)
}
defer newFileChunk.Close()
chunkInfo, err := newFileChunk.Stat()
if err != nil {
log.Fatal(err)
}
var chunkSize int64 = chunkInfo.Size()
chunkBufferBytes := make([]byte, chunkSize)
// read into chunkBufferBytes
reader := bufio.NewReader(newFileChunk)
_, err = reader.Read(chunkBufferBytes)
file.Write(chunkBufferBytes)
file.Sync() //flush to disk
chunkBufferBytes = nil // reset or empty our buffer
}
//Verify file size
filestats, err := file.Stat()
if err != nil {
log.Fatal(err)
return
}
actualFilesize := filestats.Size()
if actualFilesize != int64(length) {
log.Fatal("Actual Size: ", actualFilesize, " Expected: ", length)
return
}
file.Close()
}
下载完所有文件后,我尝试将它们重新组合成一个.zip 文件。但是,当文件放在一起时,我无法解压缩最终文件,因为它似乎已损坏。
我想知道我做错了什么,或者是否有更好的方法。提前致谢。
编辑:以下是记录到控制台的内容
bytes=0-10485759
bytes=10485760-20971519
2018/12/04 11:21:28 Actual Size: 16877828 Expected: 16877827
问题出在您的范围请求上
行
resp,_ := client.Do(req)
defer resp.Body.Close()
由 go vet
报告,因为未检查错误。如果您检查最后一个块中的响应代码,它是 416 - 这是使用的不正确范围,请更改为此
resp, err := client.Do(req)
if err != nil {
panic(err)
}
if resp.StatusCode == 416 {
fmt.Println("incorrect range")
}
defer resp.Body.Close()
我还将循环变量更改为 for i := 0; i < chunks-1; i++ {
并更改了 go routine
之后的部分
startByte = endByte + 1
endByte += 1024 * 1024 * 10
if startByte >= length {
break
}
for endByte >= length {
endByte = endByte - 1
}
并以类似的方式更改 j 循环变量
这些更改似乎对我有用,但我没有合适的测试数据来真正检查
我正在与 Accept-Ranges 和 Goroutines 并行下载一个大的 .zip 文件。该应用程序使用其范围 header.
从 URL 发送多个请求来下载 10MB 的 zip 文件块请求被分成不同的范围作为单独的 Goroutines,并将获得的数据写入临时文件。文件名为 1、2、3....
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"sync"
)
var wg sync.WaitGroup
func main() {
url := "https://path/to/large/zip/file/zipfile.zip"
res, _ := http.Head(url)
maps := res.Header
length, _ := strconv.Atoi(maps["Content-Length"][0]) // Get the content length from the header request
chunks := (length / (1024 * 1024 * 10)) + 1
// startByte and endByte determines the positions of the chunk that should be downloaded
var startByte = 0
var endByte = (1024 * 1024 * 10) - 1
//body := make([][]byte, chunks)
body := make([]io.ReadCloser, chunks)
for i := 0; i < chunks; i++ {
wg.Add(1)
go func(min int, max int, i int) {
client := &http.Client {}
req, _ := http.NewRequest("GET", url, nil)
rangeHeader := "bytes=" + strconv.Itoa(min) +"-" + strconv.Itoa(max)
fmt.Println(rangeHeader)
req.Header.Add("Range", rangeHeader)
resp,_ := client.Do(req)
defer resp.Body.Close()
reader, _ := ioutil.ReadAll(resp.Body)
body[i] = resp.Body
ioutil.WriteFile(strconv.Itoa(i), reader, 777) // Write to the file i as a byte array
wg.Done()
}(startByte, endByte, i)
startByte = endByte + 1
endByte += 1024 * 1024 * 10
}
wg.Wait()
filepath := "zipfile.zip"
// Create the file
_, err := os.Create(filepath)
if err != nil {
return
}
file, _ := os.OpenFile(filepath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
log.Fatal(err)
}
for j := 0; j < chunks; j++ {
newFileChunk, err := os.Open(strconv.Itoa(j))
if err != nil {
log.Fatal(err)
}
defer newFileChunk.Close()
chunkInfo, err := newFileChunk.Stat()
if err != nil {
log.Fatal(err)
}
var chunkSize int64 = chunkInfo.Size()
chunkBufferBytes := make([]byte, chunkSize)
// read into chunkBufferBytes
reader := bufio.NewReader(newFileChunk)
_, err = reader.Read(chunkBufferBytes)
file.Write(chunkBufferBytes)
file.Sync() //flush to disk
chunkBufferBytes = nil // reset or empty our buffer
}
//Verify file size
filestats, err := file.Stat()
if err != nil {
log.Fatal(err)
return
}
actualFilesize := filestats.Size()
if actualFilesize != int64(length) {
log.Fatal("Actual Size: ", actualFilesize, " Expected: ", length)
return
}
file.Close()
}
下载完所有文件后,我尝试将它们重新组合成一个.zip 文件。但是,当文件放在一起时,我无法解压缩最终文件,因为它似乎已损坏。
我想知道我做错了什么,或者是否有更好的方法。提前致谢。
编辑:以下是记录到控制台的内容
bytes=0-10485759
bytes=10485760-20971519
2018/12/04 11:21:28 Actual Size: 16877828 Expected: 16877827
问题出在您的范围请求上
行
resp,_ := client.Do(req)
defer resp.Body.Close()
由 go vet
报告,因为未检查错误。如果您检查最后一个块中的响应代码,它是 416 - 这是使用的不正确范围,请更改为此
resp, err := client.Do(req)
if err != nil {
panic(err)
}
if resp.StatusCode == 416 {
fmt.Println("incorrect range")
}
defer resp.Body.Close()
我还将循环变量更改为 for i := 0; i < chunks-1; i++ {
并更改了 go routine
startByte = endByte + 1
endByte += 1024 * 1024 * 10
if startByte >= length {
break
}
for endByte >= length {
endByte = endByte - 1
}
并以类似的方式更改 j 循环变量
这些更改似乎对我有用,但我没有合适的测试数据来真正检查