beyondstorage / go-storage

A vendor-neutral storage library for Golang: Write once, run on every storage service.
https://beyondstorage.io
Apache License 2.0
550 stars 53 forks source link
azblob cloud-storage dropbox files fs gcs golang qingstor s3 storage

go-storage

Website | Documentation | Community

Go dev License

Matrix Slack Telegram

Build Test Cross Build Unit Test

A vendor-neutral storage library for Golang.

Vision

Write once, run on every storage service.

Goal

Examples

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.

Features

Widely native services support

16 stable services that have passed all integration tests.

3 beta services that implemented required functions, but not passed integration tests.

4 alpha services that still under development.

More service ideas could be found at Service Integration Tracking.

Complete and easily extensible interface

Basic operations

Extended operations

Large file manipulation

// 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
```go
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)

// 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
```go
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)

Comprehensive metadata

Global object metadata

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 algorithm

Strong Typing Everywhere

Self 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

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))
}

Sponsor