Files
template-vitrine/backend/internal/modules/media/storage_local.go
T
2026-09-14 20:50:19 +02:00

52 lines
1.4 KiB
Go

package media
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// LocalStorage saves uploads under a directory on disk and serves them via
// a static file route (mounted by main.go at /uploads when this driver is
// active).
type LocalStorage struct {
dir string
baseURL string
}
func NewLocalStorage(dir, baseURL string) (*LocalStorage, error) {
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, fmt.Errorf("create upload dir: %w", err)
}
return &LocalStorage{dir: dir, baseURL: strings.TrimRight(baseURL, "/")}, nil
}
func (s *LocalStorage) Save(_ context.Context, key string, reader io.Reader, _ int64, _ string) (string, error) {
// key is always a freshly generated UUID-based name (see service.go),
// never derived from user input, so path traversal is not reachable here.
dest := filepath.Join(s.dir, filepath.Base(key))
f, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
if err != nil {
return "", fmt.Errorf("create file: %w", err)
}
defer f.Close()
if _, err := io.Copy(f, reader); err != nil {
return "", fmt.Errorf("write file: %w", err)
}
return s.baseURL + "/" + filepath.Base(key), nil
}
func (s *LocalStorage) Delete(_ context.Context, key string) error {
err := os.Remove(filepath.Join(s.dir, filepath.Base(key)))
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("delete file: %w", err)
}
return nil
}