Files
chat_room/backend/internal/service/photo_storage.go
T

62 lines
2.3 KiB
Go
Raw Normal View History

package service
import (
"fmt"
"os"
"path/filepath"
"github.com/google/uuid"
)
// PhotoStorage اینترفیس ذخیره‌سازی فایل عکس پروفایل.
// برای اتصال به یک storage واقعی (S3، Arvan Object Storage و...) کافیه یک
// پیاده‌سازی جدید از این اینترفیس بسازی و در main.go جایگزین نسخه‌ی local کنی؛
// بقیه‌ی کد (ProfileService و...) نیازی به تغییر ندارد.
type PhotoStorage interface {
// Save فایل رو ذخیره می‌کنه و URL قابل‌دسترسی (نسبی یا کامل) رو برمی‌گردونه.
Save(userID uint, ext string, data []byte) (url string, err error)
// Delete فایل رو با URL برگردونده‌شده از Save حذف می‌کنه.
Delete(url string) error
}
// LocalPhotoStorage فایل‌ها رو روی دیسک محلی سرور ذخیره می‌کنه و از طریق
// مسیر استاتیک /uploads سرو می‌شن. برای پروداکشن با تعداد کاربر بالا،
// بهتره با یک object storage واقعی جایگزین بشه (فایل‌ها روی دیسک سرور API
// می‌مونن و بین چند instance به اشتراک گذاشته نمی‌شن).
type LocalPhotoStorage struct {
rootDir string // مسیر روی دیسک، مثلاً "./uploads/photos"
urlPrefix string // پیشوند URL عمومی، مثلاً "/uploads/photos"
}
func NewLocalPhotoStorage(rootDir, urlPrefix string) *LocalPhotoStorage {
return &LocalPhotoStorage{rootDir: rootDir, urlPrefix: urlPrefix}
}
func (s *LocalPhotoStorage) Save(userID uint, ext string, data []byte) (string, error) {
userDir := filepath.Join(s.rootDir, fmt.Sprintf("%d", userID))
if err := os.MkdirAll(userDir, 0o755); err != nil {
return "", err
}
filename := uuid.NewString() + ext
fullPath := filepath.Join(userDir, filename)
if err := os.WriteFile(fullPath, data, 0o644); err != nil {
return "", err
}
return fmt.Sprintf("%s/%d/%s", s.urlPrefix, userID, filename), nil
}
func (s *LocalPhotoStorage) Delete(url string) error {
rel, err := filepath.Rel(s.urlPrefix, url)
if err != nil {
return err
}
fullPath := filepath.Join(s.rootDir, rel)
err = os.Remove(fullPath)
if os.IsNotExist(err) {
return nil
}
return err
}