I am downloading a large file in concurrent Chunks of 10MB using GO as shown below.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
)
func main() {
chunkSize := 1024 * 1024 * 10 // 10MB
url := "http://path/to/large/zip/file/zipfile.zip"
filepath := "zipfile.zip"
res, _ := http.Head(url)
maps := res.Header
length, _ := strconv.Atoi(maps["Content-Length"][0]) // Get the content length from the header request
// startByte and endByte determines the positions of the chunk that should be downloaded
var startByte = 0
var endByte = chunkSize - 1
for startByte < length {
if endByte > length {
endByte = length - 1
}
go func(startByte, endByte int) {
client := &http.Client {}
req, _ := http.NewRequest("GET", url, nil)
rangeHeader := fmt.Sprintf("bytes=%d-%d", startByte, endByte)
req.Header.Add("Range", rangeHeader)
resp,_ := client.Do(req)
defer resp.Body.Close()
data, _ := ioutil.ReadAll(resp.Body)
addToFile(filepath, startByte, endByte, data)
}(startByte, endByte)
startByte = endByte + 1
endByte += chunkSize
}
}
func addToFile(filepath string, startByte, endByte int, data []byte) {
// TODO: write to byte range in file
}
How should I go about creating the file, and writing to a specified byte range within the file corresponding to the byte range of the chunk?
For example, if I get the data from the bytes 262144000-272629759, the addToFile function should write to 262144000-272629759 within the zipfile.zip. Then, if data from another range is obtained, that should be written to the respective range in zipfile.zip.
Figured out how to do this. Change the addToFile function as shown below.
func addToFile(filepath string, startByte int, data []byte) {
f, err := os.OpenFile(filepath, os.O_CREATE | os.O_WRONLY, os.ModeAppend)
if err != nil {
panic("File not found")
}
whence := io.SeekStart
_, err = f.Seek(int64(startByte), whence)
f.Write(data)
f.Sync() //flush to disk
f.Close()
}
For example,
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
)
func write(ws io.WriteSeeker, offset int64, p []byte) (n int, err error) {
_, err = ws.Seek(offset, io.SeekStart)
if err != nil {
return 0, err
}
n, err = ws.Write(p)
if err != nil {
return 0, err
}
return n, nil
}
func main() {
filename := `test.file`
f, err := os.Create(filename)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
defer f.Close()
buf := make([]byte, 16)
for i := range buf {
buf[i] = byte('A' + i)
}
_, err = write(f, 16, buf)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
for i := range buf {
buf[i] = byte('a' + i)
}
_, err = write(f, 0, buf)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
err = f.Close()
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
data, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Printf("%q
", data)
}
Output:
"abcdefghijklmnopABCDEFGHIJKLMNOP"