Files
soft_usite/pkg/services/oss_service.go
T
2026-05-26 21:28:35 -05:00

186 lines
4.9 KiB
Go
Executable File

package services
import (
"fmt"
"io"
"log"
"time"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)
// OSSObject representa un objeto/archivo listado en el bucket
type OSSObject struct {
Key string `json:"key"`
Size int64 `json:"size"`
LastModified time.Time `json:"last_modified"`
ETag string `json:"etag"`
}
// OSSListResult resultado paginado de listado de objetos
type OSSListResult struct {
Objects []OSSObject `json:"objects"`
CommonPrefixes []string `json:"prefixes"` // "carpetas" virtuales
IsTruncated bool `json:"is_truncated"`
NextMarker string `json:"next_marker"`
}
type OSSService struct {
Client *oss.Client
Bucket *oss.Bucket
BucketName string
Endpoint string
}
// Configuración del OSS
type OSSConfig struct {
Endpoint string
AccessKeyID string
AccessKeySecret string
BucketName string
}
// Constructor del servicio OSS
func NewOSSService(cfg OSSConfig) (*OSSService, error) {
client, err := oss.New(cfg.Endpoint, cfg.AccessKeyID, cfg.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("error creando cliente OSS: %w", err)
}
bucket, err := client.Bucket(cfg.BucketName)
if err != nil {
return nil, fmt.Errorf("error obteniendo bucket: %w", err)
}
return &OSSService{
Client: client,
Bucket: bucket,
BucketName: cfg.BucketName,
Endpoint: cfg.Endpoint,
}, nil
}
// Subir un archivo
func (s *OSSService) UploadFile(objectKey string, filePath string) error {
err := s.Bucket.PutObjectFromFile(objectKey, filePath)
if err != nil {
return fmt.Errorf("error subiendo archivo: %w", err)
}
log.Printf("Archivo '%s' subido como '%s'", filePath, objectKey)
return nil
}
// Listar archivos en el bucket
func (s *OSSService) ListFiles() ([]string, error) {
objects, err := s.Bucket.ListObjects()
if err != nil {
return nil, fmt.Errorf("error listando archivos: %w", err)
}
var keys []string
for _, obj := range objects.Objects {
keys = append(keys, obj.Key)
}
return keys, nil
}
// NewOSSServiceFromDB construye OSSService desde el último registro activo
func NewOSSServiceFromDB() (*OSSService, error) {
cfg, err := models.GetLastActiveOssApi()
if err != nil {
return nil, fmt.Errorf("error obteniendo configuración OSS activa: %w", err)
}
return NewOSSService(OSSConfig{
Endpoint: cfg.Endpoint,
AccessKeyID: cfg.AccessKeyID,
AccessKeySecret: cfg.AccessKeySecret,
BucketName: cfg.BucketName,
})
}
// Eliminar un archivo del bucket
func (s *OSSService) DeleteFile(objectKey string) error {
err := s.Bucket.DeleteObject(objectKey)
if err != nil {
return fmt.Errorf("error eliminando archivo '%s' de OSS: %w", objectKey, err)
}
log.Printf("Archivo eliminado de OSS: %s", objectKey)
return nil
}
// ListObjects lista objetos del bucket con soporte de prefijo (carpeta), delimitador y paginación.
func (s *OSSService) ListObjects(prefix, marker string, maxKeys int) (*OSSListResult, error) {
if maxKeys <= 0 || maxKeys > 1000 {
maxKeys = 100
}
opts := []oss.Option{
oss.MaxKeys(maxKeys),
oss.Delimiter("/"),
}
if prefix != "" {
opts = append(opts, oss.Prefix(prefix))
}
if marker != "" {
opts = append(opts, oss.Marker(marker))
}
resp, err := s.Bucket.ListObjects(opts...)
if err != nil {
return nil, fmt.Errorf("error listando objetos OSS: %w", err)
}
result := &OSSListResult{
IsTruncated: resp.IsTruncated,
NextMarker: resp.NextMarker,
}
for _, obj := range resp.Objects {
result.Objects = append(result.Objects, OSSObject{
Key: obj.Key,
Size: obj.Size,
LastModified: obj.LastModified,
ETag: obj.ETag,
})
}
for _, cp := range resp.CommonPrefixes {
result.CommonPrefixes = append(result.CommonPrefixes, cp)
}
return result, nil
}
// SignedURL genera una URL firmada temporal para descargar/previsualizar un objeto.
func (s *OSSService) SignedURL(objectKey string, expireSeconds int) (string, error) {
if expireSeconds <= 0 {
expireSeconds = 3600
}
url, err := s.Bucket.SignURL(objectKey, oss.HTTPGet, int64(expireSeconds))
if err != nil {
return "", fmt.Errorf("error generando URL firmada: %w", err)
}
return url, nil
}
// UploadFromReader sube un archivo desde un io.Reader con el content-type dado.
func (s *OSSService) UploadFromReader(objectKey, contentType string, r io.Reader) error {
var opts []oss.Option
if contentType != "" {
opts = append(opts, oss.ContentType(contentType))
}
err := s.Bucket.PutObject(objectKey, r, opts...)
if err != nil {
return fmt.Errorf("error subiendo objeto '%s': %w", objectKey, err)
}
log.Printf("Objeto subido a OSS: %s", objectKey)
return nil
}
// DeleteObjects elimina múltiples objetos en una sola llamada (batch).
func (s *OSSService) DeleteObjects(keys []string) error {
_, err := s.Bucket.DeleteObjects(keys)
if err != nil {
return fmt.Errorf("error eliminando objetos en batch: %w", err)
}
return nil
}