一个厂商中立的存储库。
只需一次编写,即可在任意存储服务中运行。
- 厂商中立
- 生产就绪
- 极致性能
package main
import (
"log"
"github.com/rgglez/go-storage/v5/services"
"github.com/rgglez/go-storage/v5/types"
// 添加 fs 支持
_ "github.com/rgglez/go-storage/services/fs/v4"
// 添加 s3 支持
_ "github.com/rgglez/go-storage/services/s3/v3"
// 添加 gcs 支持
_ "github.com/rgglez/go-storage/services/gcs/v3"
// 添加 azblob 支持
_ "github.com/rgglez/go-storage/services/azblob/v3"
// 更多支持,可在 BeyondStorage 下获取
_ "github.com/rgglez/go-storage/services/xxx"
)
func main() {
// 使用连接字符串初始化 storager
store, err := services.NewStoragerFromString("s3://bucket_name/path/to/workdir")
if err != nil {
log.Fatalf("service init failed: %v", err)
}
// 将 io.Reader 中的数据写入 hello.txt
n, err := store.Write("hello.txt", r, length)
// 从 hello.txt 中读取数据到 io.Writer
n, err := store.Read("hello.txt", w)
// 检查 hello.txt 是否存在并获取其元数据
o, err := store.Stat("hello.txt")
// 使用对象的函数获取元数据
length, ok := o.GetContentLength()
// List 将创建路径下对象的迭代器
it, err := store.List("path")
for {
// 使用迭代器检索下一个对象,直到迭代完成
o, err := it.Next()
if errors.Is(err, types.IterateDone) {
break
}
}
// 删除 hello.txt
err = store.Delete("hello.txt")
}
更多示例可以在 go-storage-example 找到。
目前已经有 16 个稳定的服务通过了所有的 集成测试。
- 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 个公测版本的服务已实现了所需功能,但还没有通过 集成测试。
- hdfs: Hadoop Distributed File System
- tar: tar files
- uss: UPYUN Storage Service
最后还有 4 个处于内测阶段的服务仍在开发中。
更多关于服务的想法可以在 Service Integration Tracking 找到。
基本操作
- 元数据: 获取
storager
元数据
meta := store.Metadata()
_ := meta.GetWorkDir() // 获取对象的工作目录
_, ok := meta.GetWriteSizeMaximum() // 获取写操作的最大尺寸
- 读取: 读取
对象
的内容
// 在偏移量 1024 处读取 2048 字节到 io.Writer
n, err := store.Read("path", w, pairs.WithOffset(1024), pairs.WithSize(2048))
- 写入: 将内容写入
对象
中
// 从 io.Reader 写入 2048 字节
n, err := store.Write("path", r, 2048)
- 统计: 获取
对象
元数据并检查是否存在
o, err := store.Stat("path")
if errors.Is(err, services.ErrObjectNotExist) {
// 对象不存在
}
length, ok := o.GetContentLength() // 获取对象的内容长度
- 删除: 删除一个
对象
err := store.Delete("path") // 删除对象 "路径"
- 列表: 列出给定前缀或目录中的
对象
it, err := store.List("path")
for {
o, err := it.Next()
if err != nil && errors.Is(err, types.IterateDone) {
// 列表结束
}
length, ok := o.GetContentLength() // 获取对象的内容长度
}
扩展操作
- 拷贝: 复制一个
对象
到 storager
err := store.(Copier).Copy(src, dst) // 从 src 复制一个对象到 dst
- 移动: 移动一个
对象
到 storager
err := store.(Mover).Move(src, dst) // 从 src 移动一个对象到 dst
- 链接: 为
对象
生成一个可访问的公共 url
url, err := store.(Reacher).Reach("path") // 生成一个对象的 url
- 目录:
对象
目录
o, err := store.(Direr).CreateDir("path") // 创建一个对象目录
大文件操作
- 分段: 允许进行分段上传
ms := store.(Multiparter)
// 创建一个分段对象
o, err := ms.CreateMultipart("path")
// 将 io.reader 中的 1024 字节分段写入索引 1
n, part, err := ms.WriteMultipart(o, r, 1024, 1)
// 完成分段对象创建
err := ms.CompleteMultipart(o, []*Part{part})
- 追加: 允许追加到一个对象上
as := store.(Appender)
// 创建一个可追加的对象
o, err := as.CreateAppend("path")
// 从 io.Reader 写入 1024 字节
n, err := as.WriteAppend(o, r, 1024)
// 提交一个待追加的对象
err = as.CommitAppend(o)
- 块: 允许将一个对象与块 id 进行组合
bs := store.(Blocker)
// 创建一个块对象
o, err := bs.CreateBlock("path")
// 将 io.reader 中的 1024 字节写入 id 为 ”id-abc“ 的块
n, err := bs.WriteBlock(o, r, 1024, "id-abc")
// 通过块 id 组合区块
err := bs.CombineBlock(o, []string{"id-abc"})
- 页:允许进行随机写入
ps := store.(Pager)
// 创建一个页面对象
o, err := ps.CreatePage("path")
// Write 1024 bytes from io.Reader at offset 2048
n, err := ps.WritePage(o, r, 1024, 2048)
全局对象元数据
id
: 服务中的唯一键name
: 服务工作目录的相对路径mode
: 对象的模式可以由以下几种进行组合:read
,dir
,part
以及 更多etag
: 实体标签,定义于 rfc2616content-length
: 对象的内容大小content-md5
: rfc2616 中定义的 Md5 简介content-type
: rfc2616 中定义的媒体类型last-modified
: 对象的最后更新时间
系统对象元数据
服务系统对象元数据,如 storage-class
等。
o, err := store.Stat("path")
// 通过 go-service-s3 提供的 API 获取服务系统元数据
om := s3.GetObjectSystemMetadata(o)
_ = om.StorageClass // 此对象的存储类型
_ = om.ServerSideEncryptionCustomerAlgorithm // 此对象的 sse 算法
自我维护的代码生成器 定义 有助于生成我们所有的 API、pairs 和元数据。
生成的 pairs 可用作 API 的可选参数。
func WithContentMd5(v string) Pair {
return Pair{
Key: "content_md5",
Value: v,
}
}
生成的对象元数据可用于从对象中获取内容 md5。
func (o *Object) GetContentMd5() (string, bool) {
o.stat()
if o.bit&objectIndexContentMd5 != 0 {
return o.contentMd5, true
}
return "", false
}
支持通过 system pair 和 system metadata 指定在服务端对数据进行加密编码, 并且我们可以通过 Default Pairs 来简化工作。
func NewS3SseC(key []byte) (types.Storager, error) {
defaultPairs := s3.DefaultStoragePairs{
Write: []types.Pair{
// 要求密钥的加密算法必须为 AES256
s3.WithServerSideEncryptionCustomerAlgorithm(s3.ServerSideEncryptionAes256),
// 你的 AES-256 密钥, 必须具有 32 个字节长度
s3.WithServerSideEncryptionCustomerKey(key),
},
// 现在你必须提供客户密钥才能读取加密数据
Read: []types.Pair{
// 要求密钥的加密算法必须为 AES256
s3.WithServerSideEncryptionCustomerAlgorithm(s3.ServerSideEncryptionAes256),
// 你的 AES-256 密钥, 必须具有 32 个字节长度
s3.WithServerSideEncryptionCustomerKey(key),
}}
return s3.NewStorager(..., s3.WithDefaultStoragePairs(defaultPairs))
}