storage

package
v0.62.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package storage provides file/object storage backends for the upload battery.

Three concrete backends ship, each constructed directly (there is no registry or factory indirection):

  • NewLocalStorage(dir, ...) — the local filesystem.
  • NewMemoryStorage(...) — an in-process store for tests and ephemeral data.
  • NewS3Storage(bucket, reg) — S3 (or S3-compatible) object storage.

All implement the Storage interface (a re-export of upload.Storage), which covers save / get / delete / list. LocalStorage and MemoryStorage also implement RangeGetter (re-export of upload.RangeGetter) so HTTP range requests can be answered; S3Storage declines and is instead paired with a presigner (WithPresigner) so uploads/downloads bypass the app entirely.

Content checksums: SaveWithChecksum writes an object plus a checksum sidecar so a later read can detect bit-rot or an interrupted write.

Keys are validated (DefaultKeyValidator) to reject path-traversal and other forbidden sequences before they reach a backend.

Index

Constants

This section is empty.

Variables

View Source
var ErrChecksumMismatch = errors.New("storage: checksum mismatch")

ErrChecksumMismatch is the sentinel returned (wrapped) by VerifyChecksum when an object's actual SHA-256 digest does not match the expected digest.

Functions

func VerifyChecksum added in v0.14.0

func VerifyChecksum(ctx context.Context, s Storage, key, wantSHA256 string) error

VerifyChecksum re-reads key from s and compares the content's SHA-256 digest with wantSHA256. wantSHA256 must be exactly 64 hexadecimal characters; both lower- and uppercase hex are accepted. It returns nil on a match, an error wrapping ErrChecksumMismatch (carrying the key and the got/want digests) on a mismatch, and the underlying error if the object cannot be read or wantSHA256 is malformed.

Types

type DefaultKeyValidator

type DefaultKeyValidator struct{}

DefaultKeyValidator implements basic key validation.

func (DefaultKeyValidator) ValidateKey

func (DefaultKeyValidator) ValidateKey(key string) error

ValidateKey checks that a key does not contain path traversal sequences.

type FileMeta

type FileMeta struct {
	Size       int64
	ModifiedAt time.Time
}

FileMeta holds metadata about a stored file.

type KeyValidator

type KeyValidator interface {
	ValidateKey(key string) error
}

KeyValidator validates storage keys to prevent path traversal and other attacks.

type LocalOption

type LocalOption func(*LocalStorage)

LocalOption configures a LocalStorage instance.

func WithPermissions

func WithPermissions(mode os.FileMode) LocalOption

WithPermissions sets the file permission mode for saved files.

func WithTempDir

func WithTempDir(dir string) LocalOption

WithTempDir sets a custom temporary directory for atomic writes.

type LocalStorage

type LocalStorage struct {
	BaseDir string
	// contains filtered or unexported fields
}

LocalStorage implements Storage backed by the local filesystem. Writes are atomic: data is first written to a temporary file, then renamed to the final path.

func NewLocalStorage

func NewLocalStorage(baseDir string, opts ...LocalOption) *LocalStorage

NewLocalStorage creates a LocalStorage rooted at baseDir. The directory is created if it does not exist.

func (*LocalStorage) Delete

func (ls *LocalStorage) Delete(ctx context.Context, key string) error

Delete removes the file identified by key from the filesystem. It is not an error if the file does not exist.

func (*LocalStorage) Exists

func (ls *LocalStorage) Exists(ctx context.Context, key string) (bool, error)

Exists reports whether a file exists for the given key.

func (*LocalStorage) Get

func (ls *LocalStorage) Get(ctx context.Context, key string) (io.ReadCloser, error)

Get opens the file identified by key and returns a ReadCloser for its contents.

func (*LocalStorage) GetRange added in v0.46.0

func (ls *LocalStorage) GetRange(ctx context.Context, key string) (io.ReadSeekCloser, error)

GetRange implements upload.RangeGetter, exposing the seekability the local backend already has: Get opens an *os.File and then discards Seek through the io.ReadCloser return type. Key validation runs through the same fullPath call, not a parallel one.

func (*LocalStorage) Save

func (ls *LocalStorage) Save(ctx context.Context, key string, r io.Reader) error

Save writes the contents of r to a file under BaseDir identified by key. The write is atomic: data is first written to a temporary file in the same directory, then renamed to the final path. Intermediate directories are created as needed.

type MemoryStorage

type MemoryStorage struct {
	// contains filtered or unexported fields
}

MemoryStorage implements Storage backed by an in-memory map. It is safe for concurrent use via sync.RWMutex.

func NewMemoryStorage

func NewMemoryStorage() *MemoryStorage

NewMemoryStorage creates a new empty MemoryStorage.

func (*MemoryStorage) Delete

func (ms *MemoryStorage) Delete(_ context.Context, key string) error

Delete removes the file identified by key. It is not an error if the key does not exist.

func (*MemoryStorage) Exists

func (ms *MemoryStorage) Exists(_ context.Context, key string) (bool, error)

Exists reports whether a file exists for the given key.

func (*MemoryStorage) Get

func (ms *MemoryStorage) Get(_ context.Context, key string) (io.ReadCloser, error)

Get returns a ReadCloser for the file identified by key.

func (*MemoryStorage) GetRange added in v0.46.0

func (ms *MemoryStorage) GetRange(_ context.Context, key string) (io.ReadSeekCloser, error)

GetRange implements upload.RangeGetter. The bytes are already in memory, so a *bytes.Reader satisfies Seek for free — the wrapper only supplies the no-op Close.

func (*MemoryStorage) Save

func (ms *MemoryStorage) Save(_ context.Context, key string, r io.Reader) error

Save stores the contents of r under the given key. Reads at most maxMemoryFileSize bytes to prevent unbounded memory allocation.

type Presigner

type Presigner interface {
	PresignGet(ctx context.Context, bucket, key string, expires time.Duration) (*url.URL, error)
	PresignPut(ctx context.Context, bucket, key string, expires time.Duration) (*url.URL, error)
}

Presigner generates presigned URLs for direct browser uploads/downloads.

type RangeGetter added in v0.46.0

type RangeGetter = upload.RangeGetter

RangeGetter is a re-export of upload.RangeGetter, the optional capability a backend implements to expose seekable reads so HTTP range requests can be answered. LocalStorage and MemoryStorage implement it; S3Storage declines — a network-backed store would have to buffer the whole object to satisfy Seek, and WithPresigner lets the transfer bypass the app entirely.

type ReadCloser

type ReadCloser struct {
	io.Reader
}

ReadCloser wraps an io.Reader to implement io.ReadCloser with a no-op Close.

func (ReadCloser) Close

func (ReadCloser) Close() error

Close is a no-op.

type S3Client

type S3Client interface {
	PutObject(ctx context.Context, bucket, key string, r io.Reader, size int64, contentType string) error
	GetObject(ctx context.Context, bucket, key string) (io.ReadCloser, error)
	DeleteObject(ctx context.Context, bucket, key string) error
	HeadObject(ctx context.Context, bucket, key string) (bool, error)
}

S3Client is a minimal interface for S3-compatible operations. This avoids importing the AWS SDK directly; callers provide their own implementation or use the PresignedURL field for direct browser uploads.

type S3Option

type S3Option func(*S3Storage)

S3Option configures an S3Storage instance.

func WithPresigner

func WithPresigner(p Presigner) S3Option

WithPresigner sets the URL presigner for generating presigned URLs.

func WithS3Client

func WithS3Client(client S3Client) S3Option

WithS3Client sets the S3 client implementation.

func WithS3Endpoint

func WithS3Endpoint(endpoint string) S3Option

WithS3Endpoint sets a custom S3-compatible endpoint.

type S3Storage

type S3Storage struct {
	Bucket   string
	Region   string
	Endpoint string
	Client   S3Client
	// contains filtered or unexported fields
}

S3Storage implements Storage backed by an S3-compatible object store. It uses a minimal S3Client interface so no AWS SDK is imported directly.

func NewS3Storage

func NewS3Storage(bucket, region string, opts ...S3Option) *S3Storage

NewS3Storage creates a new S3Storage for the given bucket and region. Use WithS3Client to inject an actual client before calling Save/Get/etc.

func (*S3Storage) Delete

func (s *S3Storage) Delete(ctx context.Context, key string) error

Delete removes the S3 object identified by key.

func (*S3Storage) Exists

func (s *S3Storage) Exists(ctx context.Context, key string) (bool, error)

Exists reports whether an S3 object exists for the given key.

func (*S3Storage) Get

func (s *S3Storage) Get(ctx context.Context, key string) (io.ReadCloser, error)

Get returns a ReadCloser for the S3 object identified by key.

func (*S3Storage) PresignedGetURL

func (s *S3Storage) PresignedGetURL(ctx context.Context, key string, expires time.Duration) (*url.URL, error)

PresignedGetURL returns a presigned URL for downloading the object.

func (*S3Storage) PresignedPutURL

func (s *S3Storage) PresignedPutURL(ctx context.Context, key string, expires time.Duration) (*url.URL, error)

PresignedPutURL returns a presigned URL for uploading the object directly.

func (*S3Storage) Save

func (s *S3Storage) Save(ctx context.Context, key string, r io.Reader) error

Save stores the contents of r as an S3 object with the given key.

type SaveResult added in v0.14.0

type SaveResult struct {
	// Size is the number of bytes written.
	Size int64
	// SHA256 is the lowercase hex SHA-256 digest of the stored content.
	SHA256 string
}

SaveResult reports what SaveWithChecksum wrote.

func SaveWithChecksum added in v0.14.0

func SaveWithChecksum(ctx context.Context, s Storage, key string, r io.Reader) (SaveResult, error)

SaveWithChecksum stores r under key via s.Save while teeing the stream through a SHA-256 hasher, so the content is read exactly once and no backend changes are required. It returns a SaveResult carrying the byte count and the lowercase hex digest. On a Save error it returns the zero SaveResult and the error from s.Save; the stream is not buffered in memory, so it works for arbitrarily large objects.

type Storage

type Storage = upload.Storage

Storage is a re-export of the upload.Storage interface for convenience.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL