A vendor-neutral storage library for Golang.
Write once, run on every storage service.
- Vendor agnostic
- Production ready
- High performance
package main
import (
    "log"
    "github.com/beyondstorage/go-storage/v5/services"
    "github.com/beyondstorage/go-storage/v5/types"
    // Add fs support
    _ "github.com/beyondstorage/go-storage/services/fs/v4"
    // Add s3 support
    _ "github.com/beyondstorage/go-storage/services/s3/v3"
    // Add gcs support
    _ "github.com/beyondstorage/go-storage/services/gcs/v3"
    // Add azblob support
    _ "github.com/beyondstorage/go-storage/services/azblob/v3"
    // More support could be found under BeyondStorage.
    _ "github.com/beyondstorage/go-storage/services/xxx" 
)
func main() {
    // Init a Storager from connection string. 
    store, err := services.NewStoragerFromString("s3://bucket_name/path/to/workdir")
    if err != nil {
        log.Fatalf("service init failed: %v", err)
    }
    // Write data from io.Reader into hello.txt
    n, err := store.Write("hello.txt", r, length)
    // Read data from hello.txt to io.Writer
    n, err := store.Read("hello.txt", w)
    // Stat hello.txt to check existence or get its metadata
    o, err := store.Stat("hello.txt")
    // Use object's functions to get metadata
    length, ok := o.GetContentLength()
    
    // List will create an iterator of object under path.
    it, err := store.List("path")
    
    for {
    	// Use iterator.Next to retrieve next object until we meet IterateDone.
    	o, err := it.Next()
    	if errors.Is(err, types.IterateDone) {
    		break
        }
    }
    // Delete hello.txt
    err = store.Delete("hello.txt")
}More examples could be found at go-storage-example.
16 stable services that have passed all integration tests.
- azblob: Azure Blob storage
- bos: Baidu Object Storage
- cos: Tencent Cloud Object Storage
- dropbox: Dropbox
- fs: Local file system
- ftp: FTP
- gcs: Google Cloud Storage
- gdrive: Google Drive
- ipfs: InterPlanetary File System
- kodo: qiniu kodo
- memory: data that only in memory
- minio: MinIO
- obs: Huawei Object Storage Service
- oss: Aliyun Object Storage
- qingstor: QingStor Object Storage
- s3: Amazon S3
3 beta services that implemented required functions, but not passed integration tests.
- hdfs: Hadoop Distributed File System
- tar: tar files
- uss: UPYUN Storage Service
4 alpha services that still under development.
More service ideas could be found at Service Integration Tracking.
Basic operations
- Metadata: get Storagermetadata
meta := store.Metadata()
_ := meta.GetWorkDir() // Get object WorkDir
_, ok := meta.GetWriteSizeMaximum() // Get the maximum size for write operation- Read: read Objectcontent
// Read 2048 byte at the offset 1024 into the io.Writer.
n, err := store.Read("path", w, pairs.WithOffset(1024), pairs.WithSize(2048))- Write: write content into Object
// Write 2048 byte from io.Reader
n, err := store.Write("path", r, 2048)- Stat: get Objectmetadata or check existences
o, err := store.Stat("path")
if errors.Is(err, services.ErrObjectNotExist) {
	// object is not exist
}
length, ok := o.GetContentLength() // get the object content length.- Delete: delete an Object
err := store.Delete("path") // Delete the object "path"- List: list Objectin given prefix or dir
it, err := store.List("path")
for {
	o, err := it.Next()
	if err != nil && errors.Is(err, types.IterateDone) {
        // the list is over 
    }
    length, ok := o.GetContentLength() // get the object content length.
}Extended operations
- Copy: copy a Objectinside storager
err := store.(Copier).Copy(src, dst) // Copy an object from src to dst.- Move: move a Objectinside storager
err := store.(Mover).Move(src, dst) // Move an object from src to dst.- Reach: generate a public accessible url to an Object
url, err := store.(Reacher).Reach("path") // Generate an url to the object.- Dir: Dir Objectsupport
o, err := store.(Direr).CreateDir("path") // Create a dir object.Large file manipulation
- Multipart: allow doing multipart uploads
ms := store.(Multiparter)
// Create a multipart object.
o, err := ms.CreateMultipart("path")
// Write 1024 bytes from io.Reader into a multipart at index 1
n, part, err := ms.WriteMultipart(o, r, 1024, 1)
// Complete a multipart object.
err := ms.CompleteMultipart(o, []*Part{part})- Append: allow appending to an object
as := store.(Appender)
// Create an appendable object.
o, err := as.CreateAppend("path")
// Write 1024 bytes from io.Reader.
n, err := as.WriteAppend(o, r, 1024)
// Commit an append object.
err = as.CommitAppend(o)- Block: allow combining an object with block ids
bs := store.(Blocker)
// Create a block object.
o, err := bs.CreateBlock("path")
// Write 1024 bytes from io.Reader with block id "id-abc"
n, err := bs.WriteBlock(o, r, 1024, "id-abc")
// Combine block via block ids.
err := bs.CombineBlock(o, []string{"id-abc"})- Page: allow doing random writes
ps := store.(Pager)
// Create a page object.
o, err := ps.CreatePage("path")
// Write 1024 bytes from io.Reader at offset 2048
n, err := ps.WritePage(o, r, 1024, 2048)Global object metadata
- id: unique key in service
- name: relative path towards service's work dir
- mode: object mode can be a combination of- read,- dir,- partand more
- etag: entity tag as defined in rfc2616
- content-length: object's content size.
- content-md5: md5 digest as defined in rfc2616
- content-type: media type as defined in rfc2616
- last-modified: object's last updated time.
System object metadata
Service system object metadata like storage-class and so on.
o, err := store.Stat("path")
// Get service system metadata via API provides by go-service-s3.
om := s3.GetObjectSystemMetadata(o)
_ = om.StorageClass // this object's storage class
_ = om.ServerSideEncryptionCustomerAlgorithm // this object's sse algorithmSelf maintained codegen definitions helps to generate all our APIs, pairs and metadata.
Generated pairs which can be used as API optional arguments.
func WithContentMd5(v string) Pair {
    return Pair{
        Key:   "content_md5",
        Value: v,
    }
}Generated object metadata which can be used to get content md5 from object.
func (o *Object) GetContentMd5() (string, bool) {
    o.stat()
    
    if o.bit&objectIndexContentMd5 != 0 {
        return o.contentMd5, true
    }
    
    return "", false
}Server-Side Encrypt supports via system pair and system metadata, and we can use Default Pairs to simplify the job.
func NewS3SseC(key []byte) (types.Storager, error) {
    defaultPairs := s3.DefaultStoragePairs{
        Write: []types.Pair{
            // Required, must be AES256
            s3.WithServerSideEncryptionCustomerAlgorithm(s3.ServerSideEncryptionAes256),
            // Required, your AES-256 key, a 32-byte binary value
            s3.WithServerSideEncryptionCustomerKey(key),
        },
        // Now you have to provide customer key to read encrypted data
        Read: []types.Pair{
            // Required, must be AES256
            s3.WithServerSideEncryptionCustomerAlgorithm(s3.ServerSideEncryptionAes256),
            // Required, your AES-256 key, a 32-byte binary value
            s3.WithServerSideEncryptionCustomerKey(key),
        }}
    
    return s3.NewStorager(..., s3.WithDefaultStoragePairs(defaultPairs))
}