100 lines
3.0 KiB
Go
100 lines
3.0 KiB
Go
|
|
package aliyun
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"git.apinb.com/ops/files/internal/storage"
|
||
|
|
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Open 返回对象存储中的只读内容流。
|
||
|
|
func (c *Client) Open(ctx context.Context, objectKey string, byteRange *storage.ByteRange) (io.ReadCloser, error) {
|
||
|
|
if strings.TrimSpace(objectKey) == "" {
|
||
|
|
return nil, fmt.Errorf("对象键不能为空")
|
||
|
|
}
|
||
|
|
request := &oss.GetObjectRequest{
|
||
|
|
Bucket: oss.Ptr(c.bucket),
|
||
|
|
Key: oss.Ptr(objectKey),
|
||
|
|
}
|
||
|
|
if byteRange != nil {
|
||
|
|
rangeHeader := fmt.Sprintf("bytes=%d-%d", byteRange.Start, byteRange.End)
|
||
|
|
request.Range = &rangeHeader
|
||
|
|
}
|
||
|
|
result, err := c.client.GetObject(ctx, request)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("读取对象: %w", err)
|
||
|
|
}
|
||
|
|
if result == nil || result.Body == nil {
|
||
|
|
return nil, fmt.Errorf("对象内容为空")
|
||
|
|
}
|
||
|
|
return result.Body, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *Client) Stat(ctx context.Context, objectKey string) (storage.ObjectInfo, error) {
|
||
|
|
if strings.TrimSpace(objectKey) == "" {
|
||
|
|
return storage.ObjectInfo{}, fmt.Errorf("对象键不能为空")
|
||
|
|
}
|
||
|
|
|
||
|
|
result, err := c.client.GetObjectMeta(ctx, &oss.GetObjectMetaRequest{
|
||
|
|
Bucket: oss.Ptr(c.bucket),
|
||
|
|
Key: oss.Ptr(objectKey),
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
var serviceError *oss.ServiceError
|
||
|
|
if errors.As(err, &serviceError) && serviceError.Code == "NoSuchKey" {
|
||
|
|
return storage.ObjectInfo{}, storage.ErrObjectNotFound
|
||
|
|
}
|
||
|
|
return storage.ObjectInfo{}, fmt.Errorf("查询对象元数据: %w", err)
|
||
|
|
}
|
||
|
|
if result == nil {
|
||
|
|
return storage.ObjectInfo{}, fmt.Errorf("对象元数据为空")
|
||
|
|
}
|
||
|
|
if result.ContentLength < 0 {
|
||
|
|
return storage.ObjectInfo{}, fmt.Errorf("对象大小无效: %d", result.ContentLength)
|
||
|
|
}
|
||
|
|
if result.ETag == nil {
|
||
|
|
return storage.ObjectInfo{}, fmt.Errorf("对象 ETag 缺失")
|
||
|
|
}
|
||
|
|
etag := strings.TrimSpace(strings.Trim(strings.TrimSpace(*result.ETag), "\""))
|
||
|
|
if etag == "" {
|
||
|
|
return storage.ObjectInfo{}, fmt.Errorf("对象 ETag 无效")
|
||
|
|
}
|
||
|
|
if result.LastModified == nil || result.LastModified.IsZero() {
|
||
|
|
return storage.ObjectInfo{}, fmt.Errorf("对象最后修改时间缺失")
|
||
|
|
}
|
||
|
|
|
||
|
|
return storage.ObjectInfo{
|
||
|
|
Size: result.ContentLength,
|
||
|
|
StorageETag: etag,
|
||
|
|
LastModified: *result.LastModified,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *Client) Delete(ctx context.Context, objectKey string) error {
|
||
|
|
if strings.TrimSpace(objectKey) == "" {
|
||
|
|
return fmt.Errorf("对象键不能为空")
|
||
|
|
}
|
||
|
|
|
||
|
|
_, err := c.client.DeleteObject(ctx, &oss.DeleteObjectRequest{
|
||
|
|
Bucket: oss.Ptr(c.bucket),
|
||
|
|
Key: oss.Ptr(objectKey),
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("删除对象: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *Client) Status(ctx context.Context) (storage.RuntimeStatus, error) {
|
||
|
|
_, err := c.client.ListObjectsV2(ctx, &oss.ListObjectsV2Request{Bucket: oss.Ptr(c.bucket), MaxKeys: 1})
|
||
|
|
if err != nil {
|
||
|
|
return storage.RuntimeStatus{Provider: c.Provider(), Container: c.Container()}, fmt.Errorf("检查 OSS 存储状态: %w", err)
|
||
|
|
}
|
||
|
|
return storage.RuntimeStatus{Provider: c.Provider(), Container: c.Container(), Writable: true}, nil
|
||
|
|
}
|