bsw

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2026 License: MIT Imports: 4 Imported by: 0

README

bsw

Block storage wrapper: one interface to issue presigned upload and download URLs for different object storages.

Package Storage Single upload/download Multipart upload
s3 Amazon S3 and S3-compatible (MinIO, etc.) yes yes
azure Azure Blob Storage yes (SAS URL) not supported
fs Local file system + HTTP endpoints (vatel) yes (signed token) not supported
go get github.com/axkit/bsw

Requires Go 1.26 or newer.

Usage

svc := s3.New(&s3.Config{Region: aws.String("eu-central-1") /* , Credentials... */})
if err := svc.Init(ctx); err != nil {
	return err
}

o := bsw.NewObject(svc, "bucket", "dir/report.pdf").SetMetadata("owner", "42")

uploadURL, err := o.UploadURL(15 * time.Minute)
downloadURL, err := o.DownloadURL(15 * time.Minute)

Multipart upload (S3 only):

o := bsw.NewObject(svc, "bucket", "video.mp4", bsw.WithMultiParts(3))
urls, uploadID, err := o.MultipartUploadURLs(time.Hour)

// the client uploads every part with PUT and collects the ETag response headers

err = o.CompleteMultipartUpload(uploadID, []bsw.CompletedPart{
	&bsw.Part{ETag: etag1, PartNumber: 1},
	&bsw.Part{ETag: etag2, PartNumber: 2},
	&bsw.Part{ETag: etag3, PartNumber: 3},
})

Storages that do not support multipart upload return bsw.ErrNotSupported, which also matches errors.ErrUnsupported.

Storages

s3

Config.Endpoint and Config.ForcePathStyle allow using S3-compatible storages. The URL timeout must be positive and not longer than 7 days. If generating part URLs fails after the multipart upload was created, the upload is aborted. CompleteMultipartUpload accepts parts in any order and rejects nil parts, empty ETags, part numbers outside 1..10000 and duplicates.

azure

All objects are stored in one container, the bucket is the beginning of the blob name: <container>/<bucket>/<key>; it may have several elements, e.g. org1/docs. Config.ServiceURL overrides the service URL, e.g. for Azurite.

fs

fs.Service issues signed tokens instead of URLs. PreSignPutObjectURL returns a token for POST /api/v1/bos/upload?dest=<token> (multipart form, file in the file field; dest may also be a form field), PreSignGetObjectURL a token for GET /api/v1/bos/download?src=<token>. The endpoints are provided by fs.FileSystemStorageServer.Endpoints().

A token is base64url(payload).base64url(HMAC-SHA256(payload)), the payload holds the operation, bucket, key and expiration time. A token can not be changed without the key, and an upload token can not be used for download. Config.URLEncryptionKey must be at least 32 bytes long; use a random value.

Files are stored as <basePath>/<bucket>/<key>; the bucket may be a nested directory like org1/docs. Buckets and keys with .., . or empty path elements, absolute paths and paths with backslashes are rejected; on Windows also reserved names (NUL, ...), : and names ending with a dot or space. All file operations go through os.Root, so a symlink inside the base path can not lead outside of it.

On case-insensitive file systems (Windows, macOS by default) names that differ only in case refer to the same file: tokens for TenantA/report and tenanta/report give access to the same object. Use case-normalized bucket names and keys, or a case-sensitive file system, if this matters. An upload replaces the existing file atomically. A download is streamed from the file, it is not read into memory. The upload size is limited by the fasthttp server setting MaxRequestBodySize.

Errors

Errors are axkit/errors templates; check them with errors.Is(err, bsw.ErrInvalidArgument). Codes:

Code Template HTTP
BSW-0001 bsw.ErrWrongInvocation 500
BSW-0002 bsw.ErrInvalidConfig 500
BSW-0003 bsw.ErrNotSupported 501
BSW-0004 bsw.ErrInvalidArgument 400
BSW-0005 bsw.ErrNotInitialized 500
BSW-0201..0205 s3.ErrSessionInit, ErrCreateMultipartUpload, ErrPresignPartURL, ErrCompleteMultipartUpload, ErrPresignURL 500
BSW-0301..0304 azure.ErrCredential (500), ErrServiceClient, ErrPresignPutURL, ErrPresignGetURL (503)
BSW-0401 fs.ErrBasePathNotFound 500
BSW-0402 fs.ErrInvalidSignedURL 403
BSW-0403 fs.ErrValidation 400
BSW-0404 fs.ErrSignedURLExpired 403
BSW-0405 fs.ErrObjectNotFound 404
BSW-0406 fs.ErrStorage 500

Upgrading from v0

  • Go 1.26 is required.
  • fs: the endpoints require vatel v1.26 or newer (v0.14 is not supported); see its MIGRATION.md for the application side.
  • github.com/axkit/errors v1 is required; error values are templates now.
  • fs: tokens are HMAC-signed instead of AES-CFB encrypted, tokens issued by older versions are rejected. URLEncryptionKey must be at least 32 bytes. DecodeSignedURL takes the expected operation (fs.OpPut or fs.OpGet). Config.RetryCount is removed. WriteObject takes an io.Reader.
  • s3.AwsCompletedPart, azure.AzureCompletedPart and fs.AwsCompletedPart are aliases of bsw.Part.
  • azure and fs return bsw.ErrNotSupported for multipart upload instead of empty results.

License

MIT

Documentation

Overview

Package bsw (block storage wrapper) provides a common interface for generating presigned upload and download URLs for different object storages: Amazon S3 (package s3), Azure Blob Storage (package azure) and the local file system (package fs).

Index

Constants

View Source
const MaxParts = 10000

MaxParts is the maximum number of parts in a multipart upload.

Variables

View Source
var (
	// ErrWrongInvocation is returned when a method is called for an object
	// that does not support it, e.g. UploadURL for a multipart object.
	ErrWrongInvocation = errors.Template("wrong invocation").
						Code("BSW-0001").StatusCode(500).Severity(errors.Critical)

	// ErrInvalidConfig is returned when a storage wrapper is misconfigured.
	ErrInvalidConfig = errors.Template("invalid storage configuration").
						Code("BSW-0002").StatusCode(500).Severity(errors.Critical)

	// ErrNotSupported is returned when a storage does not support an operation.
	// It wraps errors.ErrUnsupported, so errors.Is(err, errors.ErrUnsupported) is true.
	ErrNotSupported = errors.Template("operation not supported").
					Code("BSW-0003").StatusCode(501).Severity(errors.Medium)

	// ErrInvalidArgument is returned when an argument, e.g. object key or
	// number of parts, is not valid.
	ErrInvalidArgument = errors.Template("invalid argument").
						Code("BSW-0004").StatusCode(400).Severity(errors.Tiny)

	// ErrNotInitialized is returned when a storage wrapper is used before Init.
	ErrNotInitialized = errors.Template("storage is not initialized").
						Code("BSW-0005").StatusCode(500).Severity(errors.Critical)
)

Functions

func ValidateObjectPath added in v0.2.0

func ValidateObjectPath(bucket, key string) error

ValidateObjectPath checks that bucket and key can be safely used as a path: both are relative slash-separated paths without empty, "." or ".." elements and without backslashes, so bucket may be a directory like "org1/docs". It is used by storages where bucket and key are joined into one path (fs, azure), so an object cannot point outside of the storage root and a key cannot point outside of its bucket.

func ValidateParts added in v0.2.0

func ValidateParts(parts int) error

ValidateParts checks that the number of parts is between 1 and MaxParts.

Types

type BlockStorageWrapper

type BlockStorageWrapper interface {
	Name() string
	PreSignPutObjectURL(o *Object, timeout time.Duration) (string, error)
	PreSignMultipartObjectURL(o *Object, timeout time.Duration) (urls []string, uploadID string, err error)
	CompleteMultipartUpload(o *Object, uploadID string, parts []CompletedPart) error
	PreSignGetObjectURL(o *Object, timeout time.Duration) (string, error)
}

BlockStorageWrapper is implemented by every supported storage.

type CompletedPart

type CompletedPart interface {
	ETagPtr() *string
	PartNumberPtr() *int64
}

CompletedPart describes an uploaded part of a multipart upload.

type Object

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

Object is an object in a storage.

func NewObject

func NewObject(w BlockStorageWrapper, bucket, key string, opts ...Option) *Object

NewObject returns an object stored in storage w.

func (*Object) Bucket

func (o *Object) Bucket() string

Bucket returns the bucket name.

func (*Object) CompleteMultipartUpload added in v0.2.0

func (o *Object) CompleteMultipartUpload(uploadID string, parts []CompletedPart) error

CompleteMultipartUpload merges uploaded parts into a single object.

func (*Object) DownloadURL added in v0.2.0

func (o *Object) DownloadURL(timeout time.Duration) (string, error)

DownloadURL returns presigned URL for GET object request.

func (*Object) Key

func (o *Object) Key() string

Key returns the object key.

func (*Object) Metadata

func (o *Object) Metadata() map[string]*string

Metadata returns object metadata.

func (*Object) MultipartUploadURLs

func (o *Object) MultipartUploadURLs(timeout time.Duration) (urls []string, uploadID string, err error)

MultipartUploadURLs returns presigned URLs for PUT object request by parts. When all parts uploaded, call CompleteMultipartUpload to merge parts into a single file.

func (*Object) Parts

func (o *Object) Parts() int

Parts returns the number of parts for a multipart upload.

func (*Object) ReplaceMetadata

func (o *Object) ReplaceMetadata(m map[string]*string) *Object

ReplaceMetadata replaces object metadata.

func (*Object) SetMetadata

func (o *Object) SetMetadata(key, value string) *Object

SetMetadata sets a metadata value.

func (*Object) SetValidTill

func (o *Object) SetValidTill(t int64) *Object

SetValidTill sets the time (unix seconds) the object is valid till.

func (*Object) StillValid

func (o *Object) StillValid() bool

StillValid returns false if the validity time is set and has passed.

func (*Object) UploadURL

func (o *Object) UploadURL(timeout time.Duration) (string, error)

UploadURL returns presigned URL for PUT object request.

func (*Object) ValidTill added in v0.2.0

func (o *Object) ValidTill() int64

ValidTill returns the time (unix seconds) the object is valid till, 0 if not set.

type Option added in v0.1.0

type Option func(*Object)

Option configures an Object.

func WithMetadata added in v0.1.0

func WithMetadata(m map[string]*string) Option

WithMetadata sets object metadata.

func WithMultiParts added in v0.1.0

func WithMultiParts(parts int) Option

WithMultiParts sets the number of parts for a multipart upload.

func WithValidTill added in v0.1.0

func WithValidTill(t int64) Option

WithValidTill sets the time (unix seconds) the object is valid till.

type Part added in v0.2.0

type Part struct {
	// Entity tag returned when the part was uploaded.
	ETag string

	// Part number that identifies the part. This is a positive integer between
	// 1 and 10,000.
	PartNumber int64
}

Part is the default implementation of CompletedPart.

func (*Part) ETagPtr added in v0.2.0

func (p *Part) ETagPtr() *string

ETagPtr returns a pointer to the entity tag.

func (*Part) PartNumberPtr added in v0.2.0

func (p *Part) PartNumberPtr() *int64

PartNumberPtr returns a pointer to the part number.

Directories

Path Synopsis
Package azure implements bsw.BlockStorageWrapper for Azure Blob Storage.
Package azure implements bsw.BlockStorageWrapper for Azure Blob Storage.
Package fs implements bsw.BlockStorageWrapper for the local file system.
Package fs implements bsw.BlockStorageWrapper for the local file system.
Package s3 implements bsw.BlockStorageWrapper for Amazon S3 and S3-compatible storages.
Package s3 implements bsw.BlockStorageWrapper for Amazon S3 and S3-compatible storages.

Jump to

Keyboard shortcuts

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