2021-12-08 19:58:13 +00:00
|
|
|
package filestore
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2021-12-13 15:51:40 +00:00
|
|
|
"strings"
|
2021-12-08 19:58:13 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// FileSystemStore is a file store that stores files on the local filesystem.
|
|
|
|
// It is currently intended for usage in a development environment.
|
|
|
|
type FileSystemStore struct {
|
|
|
|
rootPath string
|
|
|
|
baseURL *url.URL
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFileSystemStore creates a new FileSystemStore. It accepts a root path,
|
|
|
|
// which is the storage location on the local file system for stored objects,
|
|
|
|
// and a baseURL which is a URL which should be configured to serve the stored
|
|
|
|
// files over HTTP.
|
|
|
|
func NewFileSystemStore(rootPath string, baseURL string) (*FileSystemStore, error) {
|
|
|
|
url, err := url.Parse(baseURL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error parsing URL: %v", err)
|
|
|
|
}
|
2021-12-13 15:51:40 +00:00
|
|
|
if !strings.HasSuffix(url.Path, "/") {
|
|
|
|
url.Path += "/"
|
|
|
|
}
|
2021-12-08 19:58:13 +00:00
|
|
|
return &FileSystemStore{rootPath: rootPath, baseURL: url}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetObject retrieves an object from the local filesystem.
|
|
|
|
func (s *FileSystemStore) GetObject(ctx context.Context, key string) (io.ReadCloser, error) {
|
|
|
|
path := filepath.Join(s.rootPath, key)
|
|
|
|
fptr, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error opening file: %v", err)
|
|
|
|
}
|
|
|
|
return fptr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type readCloser struct {
|
|
|
|
io.Reader
|
|
|
|
io.Closer
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetObjectWithRange retrieves an object from the local filesystem with the given byte range.
|
|
|
|
func (s *FileSystemStore) GetObjectWithRange(ctx context.Context, key string, start, end int64) (io.ReadCloser, error) {
|
|
|
|
path := filepath.Join(s.rootPath, key)
|
|
|
|
fptr, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error opening file: %v", err)
|
|
|
|
}
|
2021-12-13 20:30:37 +00:00
|
|
|
_, err = fptr.Seek(start, io.SeekStart)
|
2021-12-08 19:58:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error seeking in file: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return readCloser{
|
|
|
|
Reader: io.LimitReader(fptr, end-start),
|
|
|
|
Closer: fptr,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetURL returns an HTTP URL for the provided file path.
|
|
|
|
func (s *FileSystemStore) GetURL(ctx context.Context, key string) (string, error) {
|
2021-12-09 03:05:34 +00:00
|
|
|
var url url.URL
|
|
|
|
url.Scheme = s.baseURL.Scheme
|
2021-12-08 19:58:13 +00:00
|
|
|
url.Host = s.baseURL.Host
|
|
|
|
url.Path = fmt.Sprintf("%s%s", s.baseURL.Path, key)
|
|
|
|
return url.String(), nil
|
|
|
|
}
|
|
|
|
|
2022-01-03 12:32:39 +00:00
|
|
|
// PutObject writes an object to the local filesystem.
|
|
|
|
func (s *FileSystemStore) PutObject(ctx context.Context, key string, r io.Reader, _ string) (int64, error) {
|
2021-12-08 19:58:13 +00:00
|
|
|
path := filepath.Join(s.rootPath, key)
|
|
|
|
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
|
|
|
|
return 0, fmt.Errorf("error creating directories: %v", err)
|
|
|
|
}
|
|
|
|
fptr, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
return 0, fmt.Errorf("error opening file: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err := io.Copy(fptr, r)
|
|
|
|
if err != nil {
|
|
|
|
return n, fmt.Errorf("error writing file: %v", err)
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
}
|