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 }