2025-09-25 10:45:27 +03:00

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
}