Initial open source release

This commit is contained in:
A 2026-06-04 01:37:39 +08:00
commit 74992e893f
377 changed files with 118084 additions and 0 deletions

View file

@ -0,0 +1,105 @@
package files
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
)
// BlobBackend 是 blob 字节内容的存储后端。第一阶段只有本地磁盘实现。
// 内容寻址:Put 返回的 objectKey 是内容 sha256,相同内容自动去重。
type BlobBackend interface {
Name() string
Put(ctx context.Context, data []byte) (objectKey string, err error)
Get(ctx context.Context, objectKey string) ([]byte, error)
// GetRange 只读 [offset, offset+limit) 段并返回该段字节与文件总大小(limit<=0 读到末尾),
// 避免大文件每个 chunk 都整文件读入内存(getFile 按 chunk 多次请求 ⇒ 否则 O(N²) 放大)。
GetRange(ctx context.Context, objectKey string, offset, limit int64) (data []byte, total int64, err error)
}
// LocalFS 把 blob 字节存到本地磁盘根目录下,路径按内容 hash 两级 fanout。
type LocalFS struct {
root string
}
// NewLocalFS 创建本地磁盘 blob backend,确保根目录存在。
func NewLocalFS(root string) (*LocalFS, error) {
if root == "" {
return nil, fmt.Errorf("blob root dir is empty")
}
if err := os.MkdirAll(root, 0o755); err != nil {
return nil, fmt.Errorf("create blob root %q: %w", root, err)
}
return &LocalFS{root: root}, nil
}
// Name 返回后端标识,与 file_blobs.backend 一致。
func (l *LocalFS) Name() string { return "localfs" }
func (l *LocalFS) pathFor(objectKey string) string {
if len(objectKey) < 4 {
return filepath.Join(l.root, "_", objectKey)
}
return filepath.Join(l.root, objectKey[:2], objectKey[2:4], objectKey)
}
// Put 写入内容并返回 sha256 hex 作为 objectKey;同内容已存在则跳过写入(去重)。
func (l *LocalFS) Put(_ context.Context, data []byte) (string, error) {
sum := sha256.Sum256(data)
key := hex.EncodeToString(sum[:])
path := l.pathFor(key)
if _, err := os.Stat(path); err == nil {
return key, nil
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return "", fmt.Errorf("create blob dir: %w", err)
}
tmp := path + ".tmp"
if err := os.WriteFile(tmp, data, 0o644); err != nil {
return "", fmt.Errorf("write blob: %w", err)
}
if err := os.Rename(tmp, path); err != nil {
return "", fmt.Errorf("commit blob: %w", err)
}
return key, nil
}
// Get 读取 objectKey 对应的全部字节。
func (l *LocalFS) Get(_ context.Context, objectKey string) ([]byte, error) {
return os.ReadFile(l.pathFor(objectKey))
}
// GetRange 用 ReadAt 只读 [offset, offset+limit) 段,total 取自文件大小;
// n 受 total 约束,故即便客户端传超大 limit 也只分配文件实际大小,不会按客户端巨值分配。
func (l *LocalFS) GetRange(_ context.Context, objectKey string, offset, limit int64) ([]byte, int64, error) {
f, err := os.Open(l.pathFor(objectKey))
if err != nil {
return nil, 0, err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return nil, 0, err
}
total := info.Size()
if offset < 0 {
offset = 0
}
if offset >= total {
return []byte{}, total, nil
}
n := total - offset
if limit > 0 && limit < n {
n = limit
}
buf := make([]byte, n)
read, err := f.ReadAt(buf, offset)
if err != nil && err != io.EOF {
return nil, 0, err
}
return buf[:read], total, nil
}