83 lines
2.1 KiB
Go
83 lines
2.1 KiB
Go
|
|
// download.go fetches a remote file (presigned MinIO URL or any HTTP URL) and
|
||
|
|
// saves it to a local path. Uses stdlib only — no external HTTP client needed.
|
||
|
|
package runner
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"net/http"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
)
|
||
|
|
|
||
|
|
// DownloadFile fetches the resource at rawURL and writes it to destPath,
|
||
|
|
// creating parent directories as needed. Returns the number of bytes written.
|
||
|
|
func DownloadFile(ctx context.Context, rawURL, destPath string) (int64, error) {
|
||
|
|
if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil {
|
||
|
|
return 0, fmt.Errorf("mkdir: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("new request: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
resp, err := http.DefaultClient.Do(req)
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("GET: %w", err)
|
||
|
|
}
|
||
|
|
defer resp.Body.Close()
|
||
|
|
|
||
|
|
if resp.StatusCode != http.StatusOK {
|
||
|
|
return 0, fmt.Errorf("server returned %d", resp.StatusCode)
|
||
|
|
}
|
||
|
|
|
||
|
|
f, err := os.Create(destPath)
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("create file: %w", err)
|
||
|
|
}
|
||
|
|
defer f.Close()
|
||
|
|
|
||
|
|
n, err := io.Copy(f, resp.Body)
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("write: %w", err)
|
||
|
|
}
|
||
|
|
return n, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// UploadFile PUTs a local file to a presigned MinIO/S3 URL.
|
||
|
|
// MinIO presigned PUT expects the raw bytes in the request body with
|
||
|
|
// Content-Type application/octet-stream.
|
||
|
|
func UploadFile(ctx context.Context, rawURL, filePath string) (int64, error) {
|
||
|
|
f, err := os.Open(filePath)
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("open: %w", err)
|
||
|
|
}
|
||
|
|
defer f.Close()
|
||
|
|
|
||
|
|
stat, err := f.Stat()
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("stat: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, f)
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("new request: %w", err)
|
||
|
|
}
|
||
|
|
req.ContentLength = stat.Size()
|
||
|
|
req.Header.Set("Content-Type", "application/octet-stream")
|
||
|
|
|
||
|
|
resp, err := http.DefaultClient.Do(req)
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("PUT: %w", err)
|
||
|
|
}
|
||
|
|
defer resp.Body.Close()
|
||
|
|
|
||
|
|
// MinIO returns 200 on successful PUT of presigned objects
|
||
|
|
if resp.StatusCode >= 300 {
|
||
|
|
return 0, fmt.Errorf("upload server returned %d", resp.StatusCode)
|
||
|
|
}
|
||
|
|
return stat.Size(), nil
|
||
|
|
}
|