Files
filestore/remote/miniostorage/miniostorage.go
T
2025-10-29 00:59:52 +03:00

127 lines
3.0 KiB
Go

package miniostorage
import (
"context"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"git.company.lan/gopkg/filestore/remote"
)
// Убеждаемся в том, что мы всегда реализуем интерфейс remote.Storage.
var _ remote.Storage = (*MinioStorage)(nil)
func init() {
remote.Register("minio", &MinioStorage{})
}
type MinioStorage struct {
ctx context.Context
client *minio.Client
bucket string
}
func (s *MinioStorage) NewStorage(ctx context.Context, connString string) (remote.Storage, error) {
u, err := url.Parse(connString)
if err != nil {
return nil, err
}
queries := u.Query()
username, password := getUserPassword(u)
token := queries.Get("token")
opts := &minio.Options{
Creds: credentials.NewStaticV4(username, password, token),
Region: "us-east-1",
}
if queries.Has("secure") {
secure, err := strconv.ParseBool(queries.Get("secure"))
if err != nil {
return nil, err
}
opts.Secure = secure
}
if queries.Has("region") {
opts.Region = queries.Get("region")
}
client, err := minio.New(u.Host, opts)
if err != nil {
return nil, err
}
s.ctx = ctx
s.client = client
s.bucket = u.Path[1:]
return s, nil
}
func (s *MinioStorage) Create(path string, opts ...remote.Option) (io.WriteCloser, error) {
return newMinioWriter(s.ctx, s.client, s.bucket, path, opts...), nil
}
func (s *MinioStorage) Open(name string) (http.File, error) {
obj, err := s.client.GetObject(s.ctx, s.bucket, name, minio.GetObjectOptions{})
if err != nil {
return nil, err
}
return &minioFileWrapper{Object: obj, name: name}, nil
}
func (s *MinioStorage) Remove(path string) error {
return s.client.RemoveObject(s.ctx, s.bucket, path, minio.RemoveObjectOptions{})
}
func (s *MinioStorage) Stat(path string) (remote.FileInfo, error) {
info, err := s.client.StatObject(s.ctx, s.bucket, path, minio.StatObjectOptions{})
if err != nil {
return nil, err
}
return newMinioFileInfo(info), nil
}
func (s *MinioStorage) Exists(path string) (bool, error) {
// Сначала проверяем, является ли путь файлом
if ok, err := s.IsFile(path); err == nil && ok {
return true, nil
}
// Если не файл, то проверяем, является ли путь каталогом
return s.IsDir(path)
}
func (s *MinioStorage) IsDir(path string) (bool, error) {
options := minio.ListObjectsOptions{
Prefix: strings.TrimRight(path, "/") + "/",
Recursive: false,
MaxKeys: 1,
}
objectChan := s.client.ListObjects(s.ctx, s.bucket, options)
object, ok := <-objectChan
if !ok {
return false, nil
}
if object.Err != nil {
return false, object.Err
}
return true, nil
}
func (s *MinioStorage) IsFile(path string) (bool, error) {
_, err := s.client.StatObject(s.ctx, s.bucket, path, minio.StatObjectOptions{})
if err == nil {
return true, nil
}
if strings.Contains(err.Error(), "The specified key does not exist.") {
return false, nil
}
return false, err
}