82 lines
1.6 KiB
Go
82 lines
1.6 KiB
Go
package downloader
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
func DownloadFile(url, filepath string, silent bool) error {
|
|
var start int64
|
|
if info, err := os.Stat(filepath); err == nil {
|
|
start = info.Size()
|
|
}
|
|
|
|
out, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
if start > 0 {
|
|
if _, err := out.Seek(start, 0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// HTTP request with Range header for resume
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if start > 0 {
|
|
req.Header.Set("Range", "bytes="+strconv.FormatInt(start, 10)+"-")
|
|
}
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == http.StatusNotFound {
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Get total size for progress calculation
|
|
var total int64
|
|
if resp.StatusCode == http.StatusPartialContent {
|
|
contentRange := resp.Header.Get("Content-Range") // e.g., bytes 1234-9999/10000
|
|
var sizeStr string
|
|
fmt.Sscanf(contentRange, "bytes %*d-%*d/%s", &sizeStr)
|
|
total, _ = strconv.ParseInt(sizeStr, 10, 64)
|
|
} else if resp.ContentLength > 0 {
|
|
total = start + resp.ContentLength
|
|
}
|
|
|
|
buf := make([]byte, 32*1024)
|
|
var downloaded = start
|
|
for {
|
|
n, err := resp.Body.Read(buf)
|
|
if n > 0 {
|
|
if _, writeErr := out.Write(buf[:n]); writeErr != nil {
|
|
return writeErr
|
|
}
|
|
downloaded += int64(n)
|
|
if total > 0 && !silent {
|
|
fmt.Printf("\r%.2f%%", float64(downloaded)*100/float64(total))
|
|
}
|
|
}
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|