52 lines
1.4 KiB
Go
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
|
|
}
|