Documentation
¶
Overview ¶
Package simplecloud provides a unified interface for reading and writing objects across different storage backends, including the local filesystem, HTTP, Backblaze B2, Google Cloud Storage, and Amazon S3.
All backends implement the Reader and/or Writer interfaces, which wrap the underlying SDK into a simple NewReader/NewWriter model. Transparent compression and decompression based on file extension is available via InitReader and InitWriter.
Index ¶
- func Copy(ctx context.Context, src Reader, dst Writer, srcPath, dstPath string) (int64, error)
- func InitReader(ctx context.Context, bucket Reader, path string) (io.ReadCloser, error)
- func InitWriter(ctx context.Context, bucket Writer, path string) (io.WriteCloser, error)
- func Open(ctx context.Context, path string, opts ...OpenOption) (io.ReadCloser, error)
- type Aborter
- type B2Bucket
- type BucketResolver
- type FileBucket
- type GCSBucket
- type HTTPBucket
- type Lister
- type ObjectInfo
- type OpenOption
- func WithB2Credentials(keyID, appKey string) OpenOption
- func WithConcurrentDownloads(n int) OpenOption
- func WithGCSServiceAccount(path string) OpenOption
- func WithHTTPClient(client *http.Client) OpenOption
- func WithResolver(r BucketResolver) OpenOption
- func WithS3Credentials(accessKey, secretKey string) OpenOption
- func WithS3Endpoint(endpoint string) OpenOption
- func WithS3Region(region string) OpenOption
- type ReadWriter
- type Reader
- type S3Bucket
- type Writer
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Copy ¶ added in v0.0.5
Copy reads from srcPath on src and writes to dstPath on dst, using InitReader and InitWriter so that compression and decompression are applied automatically based on the path extensions. This means formats can be transcoded in a single call — e.g. copying a .gz source to a .xz destination will decompress and recompress on the fly.
The returned count is the number of uncompressed bytes transferred between the reader and writer, not the number of bytes read from or written to storage.
If the transfer fails partway, the destination is aborted rather than closed, so no truncated object is published — Close is what commits on the cloud backends. Destinations that do not implement Aborter are closed instead, and so will retain whatever was written.
func InitReader ¶
InitReader opens path from bucket for reading, wrapping the stream in a decompressor when the path extension is recognised:
- .gz — gzip
- .bz2 — bzip2
- .xz — xz/lzma
The path may be a full URL; only the path component is passed to the bucket. The caller must close the returned ReadCloser when done.
func InitWriter ¶
InitWriter opens path on bucket for writing, wrapping the stream in a compressor when the path extension is recognised:
- .gz — gzip
- .bz2 — bzip2
- .xz — xz/lzma
The path may be a full URL; only the path component is passed to the bucket. The caller must call Close on the returned WriteCloser when done; for cloud backends this is what commits the upload.
func Open ¶ added in v0.0.12
func Open(ctx context.Context, path string, opts ...OpenOption) (io.ReadCloser, error)
Open opens path for reading, selecting a backend from the URL scheme:
- (no scheme) — local filesystem
- http, https — HTTP(S); the scheme and host of path form the base URL
- b2 — Backblaze B2; requires WithB2Credentials
- s3 — Amazon S3 or S3-compatible; see WithS3Credentials/Endpoint/Region
- gs — Google Cloud Storage; see WithGCSServiceAccount
For b2, s3, and gs the host is the bucket name. Any other scheme must be handled by a WithResolver resolver, which is also consulted first and so can override the built-ins. Compression is applied transparently from the path extension and any query string is dropped, exactly as in InitReader.
The scheme and host come from url.Parse. A path with no host, or one that url.Parse rejects (for example a local path containing a bare '%'), is treated as a local filesystem path. Because the original path is passed to InitReader, a '#' in an object key is preserved; a remote key containing a bare '%' is not addressable through Open, so percent-encode it or construct the backend directly.
The s3 and gs schemes construct a client per call and never close it — fine for short-lived use, but long-running callers should supply a WithResolver that returns a backend whose client they manage. The caller must close the returned ReadCloser.
Types ¶
type Aborter ¶ added in v0.0.12
type Aborter interface {
// Abort discards the write. It is called instead of, not in addition to,
// Close.
Abort() error
}
Aborter is implemented by writers that can discard an in-progress write instead of committing it.
On the cloud backends Close is what publishes an object, so closing a stream whose transfer failed would commit a truncated object. Copy calls Abort in that case. Writers that cannot abort are simply closed, which does commit whatever was written.
type B2Bucket ¶
type B2Bucket struct {
// Bucket is the underlying blazer bucket handle that reads and writes go
// through.
Bucket *b2.Bucket
// ConcurrentDownloads controls how many parallel range requests are used
// when downloading large objects. Zero uses the blazer library default.
ConcurrentDownloads int
}
B2Bucket implements Reader and Writer for a Backblaze B2 bucket.
func NewB2Client ¶
NewB2Client authenticates with Backblaze B2 using accessKey and secretKey, then opens the named bucket.
func (*B2Bucket) List ¶ added in v0.0.14
List iterates over objects in the bucket whose key begins with prefix. The blazer iterator pages lazily, so abandoning it stops the requests.
Attrs is free here: the objects come back from the listing with their file info already populated, so reading them costs no extra request.
func (*B2Bucket) NewReader ¶
NewReader opens the object at path in the bucket for reading. A leading slash is stripped: blazer interpolates the name straight into the object URL path, so "/foo" would create an object literally named "/foo".
func (*B2Bucket) NewWriter ¶
NewWriter opens the object at path in the bucket for writing. A leading slash is stripped (see NewReader). The caller must call Close when done; Close finalises the upload to B2. The returned writer implements Aborter: aborting cancels the write's context, and for uploads large enough to have switched to the large-file API it also issues b2_cancel_large_file so the uploaded parts are discarded rather than left billable.
type BucketResolver ¶ added in v0.0.12
BucketResolver constructs the backend for a URL's scheme and host (the host is the URL authority, typically the bucket name). Returning a non-nil Reader selects it; returning (nil, nil) falls through to Open's built-in schemes.
A resolver is the extension point for schemes Open does not handle natively, and for taking control of client lifecycle — e.g. returning a shared GCSBucket whose *storage.Client you close yourself, rather than the per-call client the built-in gs scheme creates and never closes.
type FileBucket ¶
type FileBucket struct{}
FileBucket implements Reader and Writer against the local filesystem.
func (*FileBucket) NewReader ¶
func (f *FileBucket) NewReader(ctx context.Context, path string) (io.ReadCloser, error)
NewReader opens the file at path for reading. The context is accepted to satisfy the Reader interface but is unused: local file reads are not cancellable.
func (*FileBucket) NewWriter ¶ added in v0.0.2
func (f *FileBucket) NewWriter(ctx context.Context, path string) (io.WriteCloser, error)
NewWriter creates or truncates the file at path for writing. Any missing parent directories are created automatically. The context is accepted to satisfy the Writer interface but is unused: local file writes are not cancellable. The returned writer implements Aborter.
type GCSBucket ¶
type GCSBucket struct {
// Bucket is the underlying GCS bucket handle that reads and writes go
// through.
Bucket *storage.BucketHandle
}
GCSBucket implements Reader and Writer for a Google Cloud Storage bucket.
func NewGCSClient ¶
NewGCSClient creates a GCS client and opens the named bucket. If serviceAccountFile is non-empty it is used for authentication; otherwise Application Default Credentials are used, which works automatically in GKE, Cloud Run, and locally via `gcloud auth application-default login`. The underlying storage.Client is not exposed; callers that need to close it should construct one directly.
The file is declared to hold a service account rather than passed as an untyped credentials file, which is the non-deprecated form of the option: the untyped variant accepts any credential type, including externally sourced ones that name an executable to run. Note that the storage client resolves credentials through a path that does not currently act on the declared type, so this states the expectation rather than enforcing it.
func (*GCSBucket) List ¶ added in v0.0.14
List iterates over objects in the bucket whose key begins with prefix. The underlying iterator pages lazily, so abandoning it stops the requests.
func (*GCSBucket) NewReader ¶
NewReader opens the object at path in the bucket for reading. A leading slash is stripped so keys match the S3 and B2 backends; the GCS client would otherwise treat "/foo" as an object literally named "/foo".
func (*GCSBucket) NewWriter ¶
NewWriter opens the object at path in the bucket for writing. A leading slash is stripped (see NewReader). The caller must call Close when done; Close is what commits the object to GCS. The returned writer implements Aborter: aborting cancels the write's context, which is how the GCS client is told to discard a partial object instead of committing it.
Unlike S3 and B2, an abandoned GCS upload needs no server-side cleanup: only a completed resumable upload appears in the bucket, so partial data is never stored or billed, and the session expires on its own after a week.
type HTTPBucket ¶
type HTTPBucket struct {
// Client issues the GET requests; if nil at construction, NewHTTPBucket
// substitutes http.DefaultClient.
Client *http.Client
// URL is the base URL whose scheme, host, credentials, and path are reused
// for every request.
URL *url.URL
}
HTTPBucket implements Reader for HTTP and HTTPS sources. It does not support writes; use a different backend for upload destinations.
func NewHTTPBucket ¶
func NewHTTPBucket(client *http.Client, path string) (*HTTPBucket, error)
NewHTTPBucket constructs an HTTPBucket with the given base URL. The scheme, host, any credentials, and any base path are reused for every request; the per-call path is joined onto the base path in NewReader. If client is nil, http.DefaultClient is used.
func (*HTTPBucket) NewReader ¶
func (h *HTTPBucket) NewReader(ctx context.Context, path string) (io.ReadCloser, error)
NewReader issues a GET request for path joined onto the bucket's base URL and returns the response body. A base path, if any, is preserved as a prefix (so a base of https://host/v1 and a path of /obj.gz requests /v1/obj.gz). Non-2xx responses are returned as an error with the URL redacted. The caller must close the returned ReadCloser when done.
type Lister ¶ added in v0.0.14
type Lister interface {
// List iterates over every object whose key begins with prefix, in
// whatever order the backend returns them. A leading slash on prefix is
// stripped, and an empty prefix lists the whole bucket.
//
// Pagination is handled internally; the iterator fetches further pages as
// it is consumed, so stopping early stops the requests. On failure the
// iterator yields one final pair with a non-nil error and then ends, so a
// caller must check the error on every iteration:
//
// for obj, err := range bucket.List(ctx, "magic/") {
// if err != nil {
// return err
// }
// ...
// }
//
// Listing is flat: there is no delimiter, so keys containing "/" are
// returned in full rather than collapsed into common prefixes.
List(ctx context.Context, prefix string) iter.Seq2[ObjectInfo, error]
}
Lister is implemented by backends that can enumerate objects.
It is optional, in the same way as Aborter: the local filesystem and HTTP backends do not implement it. Type-assert to reach it.
type ObjectInfo ¶ added in v0.0.14
type ObjectInfo struct {
// Key is the object's full key, not relative to the listed prefix, and
// carries no leading slash.
Key string
// Size is the stored size in bytes. For a compressed object this is the
// compressed size, not the size InitReader will yield.
Size int64
// LastModified is the object's modification time. The backends do not all
// mean the same thing by it: S3 and GCS always report a server-side
// timestamp, while B2 only records one when the uploader supplied it and
// otherwise falls back to the upload timestamp. Treat it as "roughly when
// this object appeared", not as a value to compare across backends.
LastModified time.Time
}
ObjectInfo describes a single object returned by a List.
type OpenOption ¶ added in v0.0.12
type OpenOption func(*openOptions)
OpenOption configures Open. Options are applied in order; a later option of the same kind overrides an earlier one.
func WithB2Credentials ¶ added in v0.0.12
func WithB2Credentials(keyID, appKey string) OpenOption
WithB2Credentials sets the Backblaze B2 application key id and key used to authenticate b2 paths. They are required for the b2 scheme; the library does not source credentials from the environment on its own.
func WithConcurrentDownloads ¶ added in v0.0.12
func WithConcurrentDownloads(n int) OpenOption
WithConcurrentDownloads sets the number of parallel range requests used for b2 downloads. Zero (the default) uses the blazer library default.
func WithGCSServiceAccount ¶ added in v0.0.12
func WithGCSServiceAccount(path string) OpenOption
WithGCSServiceAccount sets the service-account JSON file used for gs paths. Empty uses Application Default Credentials.
func WithHTTPClient ¶ added in v0.0.12
func WithHTTPClient(client *http.Client) OpenOption
WithHTTPClient sets the client used for http and https paths. When unset (or set to nil) http.DefaultClient is used.
func WithResolver ¶ added in v0.0.12
func WithResolver(r BucketResolver) OpenOption
WithResolver registers a resolver consulted before the built-in schemes, so it can add new schemes or override built-in ones. Returning (nil, nil) from the resolver falls through to the built-ins.
func WithS3Credentials ¶ added in v0.0.12
func WithS3Credentials(accessKey, secretKey string) OpenOption
WithS3Credentials sets the access key and secret key used for s3 paths. If left empty, the default AWS credential chain (environment, shared config, instance role, …) is used.
func WithS3Endpoint ¶ added in v0.0.12
func WithS3Endpoint(endpoint string) OpenOption
WithS3Endpoint targets an S3-compatible endpoint (e.g. Cloudflare R2, MinIO) for s3 paths; path-style addressing is enabled automatically when set.
func WithS3Region ¶ added in v0.0.12
func WithS3Region(region string) OpenOption
WithS3Region sets the region for s3 paths. Empty defaults to "auto".
type ReadWriter ¶
ReadWriter is implemented by backends that support both reads and writes.
type Reader ¶
type Reader interface {
// NewReader opens the object at path for reading. The caller must close
// the returned ReadCloser when done.
NewReader(context.Context, string) (io.ReadCloser, error)
}
Reader is implemented by any storage backend that supports object reads.
type S3Bucket ¶ added in v0.0.6
type S3Bucket struct {
// Bucket is the name of the target S3 bucket.
Bucket string
// contains filtered or unexported fields
}
S3Bucket implements Reader and Writer for an Amazon S3 bucket (or any S3-compatible object store).
func NewS3Client ¶ added in v0.0.6
func NewS3Client(ctx context.Context, accessKey, secretKey, bucketName, endpoint, region string) (*S3Bucket, error)
NewS3Client creates an S3 client for the named bucket. accessKey and secretKey are optional; if both are empty, the default AWS credential chain is used. endpoint may be set to target S3-compatible stores (e.g. Cloudflare R2, MinIO); path-style addressing is enabled automatically when an endpoint is provided. region defaults to "auto" if empty.
func (*S3Bucket) List ¶ added in v0.0.14
List iterates over objects in the bucket whose key begins with prefix. The paginator is advanced lazily, so abandoning the iterator stops the requests.
func (*S3Bucket) NewReader ¶ added in v0.0.6
NewReader opens the object at path in the bucket for reading. A leading slash is stripped: aws-sdk-go-v2 does not canonicalise keys, and reads with leading/double slashes 404 (aws/aws-sdk-go#2559).
func (*S3Bucket) NewWriter ¶ added in v0.0.6
NewWriter opens the object at path in the bucket for writing using a background goroutine and an io.Pipe so that data is streamed to S3 without buffering the entire payload in memory. A leading slash is stripped: aws-sdk-go-v2 Upload/PutObject silently succeeds without storing anything when the key begins with a slash (aws/aws-sdk-go-v2#1701), so the strip prevents silent data loss. The caller must call Close when done; Close blocks until the upload completes and returns any upload error.
type Writer ¶
type Writer interface {
// NewWriter opens the object at path for writing. The caller must call
// Close when done; for cloud backends, Close is what commits the upload.
NewWriter(context.Context, string) (io.WriteCloser, error)
}
Writer is implemented by any storage backend that supports object writes.