Baixar arquivos grandes é sempre uma tarefa tediosa devido à latência, natureza sequencial do programa que baixa o arquivo e downloads interrompidos. HTTP tem o cabeçalho ‘Accept-Ranges’ e, se habilitado pelo servidor, permite baixar os dados de um determinado intervalo de bytes. Portanto, se tivermos um arquivo de tamanho 10.000 bytes presente no servidor. Em seguida, podemos fazer uma solicitação HEAD ao servidor para verificar o cabeçalho ‘Accept-Ranges’. A solicitação HEAD também retorna o ‘Content-Length’ sem baixar o arquivo inteiro. Portanto, podemos baixar os bytes de ‘900-1000’ enviando ‘Range = 900-1000’ no cabeçalho da solicitação GET para obter apenas esses dados. Portanto, dividimos as solicitações com intervalos como rotinas Go separadas e, em seguida, gravamos os dados em arquivos temporários. Então, se pudermos, criar 5 rotinas Go para baixar um arquivo de 414 bytes com cada um baixando 100 bytes. Será o seguinte:
- 1-100 bytes – 1
- 101-200 bytes – 2
- 201-300 bytes – 3
- 301-400 bytes – 4
- 400-414 bytes – 5
À medida que cada arquivo é baixado separadamente, lemos todos os arquivos e gravamos no arquivo de saída desejado em sequência para obter o arquivo. Os resultados revelaram-se bastante rápidos do que usar o wget para certos fatores das rotinas Go.
package main
import (
"io/ioutil"
"net/http"
"strconv"
"sync"
)
var wg sync.WaitGroup
func main() {
res, _ := http.Head("http://localhost/rand.txt"); // 187 MB file of random numbers per line
maps := res.Header
length, _ := strconv.Atoi(maps["Content-Length"][0]) // Get the content length from the header request
limit := 10 // 10 Go-routines for the process so each downloads 18.7MB
len_sub := length / limit // Bytes for each Go-routine
diff := length % limit // Get the remaining for the last request
body := make([]string, 11) // Make up a temporary array to hold the data to be written to the file
for i := 0; i < limit ; i++ {
wg.Add(1)
min := len_sub * i // Min range
max := len_sub * (i + 1) // Max range
if (i == limit - 1) {
max += diff // Add the remaining bytes in the last request
}
go func(min int, max int, i int) {
client := &http.Client {}
req, _ := http.NewRequest("GET", "http://localhost/rand.txt", nil)
range_header := "bytes=" + strconv.Itoa(min) +"-" + strconv.Itoa(max-1) // Add the data for the Range header of the form "bytes=0-100"
req.Header.Add("Range", range_header)
resp,_ := client.Do(req)
defer resp.Body.Close()
reader, _ := ioutil.ReadAll(resp.Body)
body[i] = string(reader)
ioutil.WriteFile(strconv.Itoa(i), []byte(string(body[i])), 0x777) // Write to the file i as a byte array
wg.Done()
// ioutil.WriteFile("new_oct.png", []byte(string(body)), 0x777)
}(min, max, i)
}
wg.Wait()
}
/*
alias combine="perl -E 'say for 0..10' | xargs cat > output.txt"
alias clean-dir="ls -1 | perl -ne 'print if /^d+$/' | xargs rm"
alias verify="diff /var/www/rand.txt output.txt"
Combine - read the files and append them to the text file
Clean - Remove all the files in the folder which are numbers. (Temp files)
Verify - Verify the diff of the files from the current directory to the original file
*/
/*
Results :
Parallel :
10 Go routines :
real 0m4.349s
user 0m0.484s
sys 0m0.356s
60 Go routines :
real 0m0.891s
user 0m0.484s
sys 0m0.432s
Wget :
real 0m19.536s
user 0m4.652s
sys 0m0.580s
Combine files : perl -E 'say for 0..59' | xargs cat > output.txt
real 0m1.532s
user 0m0.000s
sys 0m0.244s
*/