filestore

package module
v0.0.0-...-035b907 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 13, 2026 License: MIT Imports: 7 Imported by: 0

README

📦 پکیج FileStore – ماژول حرفه‌ای ذخیره‌سازی فایل با Golang و MinIO

یک ماژول Production-Ready، مستقل از فریم‌ورک و قابل تعویض با هر Storage Provider برای مدیریت فایل‌ها در پروژه‌های Golang.
این ماژول بر اساس اصول Clean Architecture طراحی شده و امکان تعویض MinIO با هر Storage دیگری (S3, GCS, Azure) را فراهم می‌کند.

✨ ویژگی‌ها

🏗 معماری حرفه‌ای
  • کاملاً مستقل از Gin یا هر فریم‌ورک دیگر
  • طراحی مبتنی بر Interface و Abstraction
  • قابل جایگزینی با هر storage provider
  • بدون وابستگی به HTTP layer

🆔 مدیریت حرفه‌ای شناسه‌ها

  • استفاده از UUID برای نام فایل در Storage (جلوگیری از تداخل)
  • استفاده از ULID برای شناسه public API (sortable و خواناتر)

📤 قابلیت‌های اصلی

  • آپلود فایل (Streaming، بدون بارگذاری کامل در حافظه)
  • دانلود امن فایل
  • حذف فایل
  • تولید Presigned URL با زمان انقضا
  • لیست فایل‌ها با فیلتر prefix / suffix
  • پشتیبانی از Versioning در صورت فعال بودن Bucket

🚀 عملکرد بالا

  • استفاده کامل از context.Context
  • پشتیبانی از concurrency بالا
  • مصرف بهینه حافظه برای فایل‌های حجیم
  • Retry mechanism
  • Structured Logging
  • قابل استفاده در محیط‌های High Throughput

🔐 امنیت

  • پشتیبانی از Server-Side Encryption
  • اعتبارسنجی اندازه و نوع فایل
  • Presigned URL با مدت اعتبار مشخص
  • جداسازی Domain Errors از Infra Errors

🧩 راه‌اندازی docker

docker compose up -d

🛠️ نصب

go get github.com/Skryldev/filestore
go get github.com/minio/minio-go/v7
go get github.com/google/uuid
go get github.com/oklog/ulid/v2

1️⃣ ساخت Config

cfg, err := filestore.LoadFromEnv()
if err != nil {
    log.Fatal(err)
}
یا ساخت دستی:
cfg := &filestore.Config{
    Endpoint:  "localhost:9000",
    AccessKey: "minioadmin",
    SecretKey: "minioadmin",
    UseSSL:    false,
    Bucket:    "files",
}

🚀 استفاده کامل از ماژول

2️⃣ ساخت Logger

logger := filestore.NewZapLogger()

3️⃣ ساخت Storage

storage, err := minioadapter.New(cfg, logger)
if err != nil {
    log.Fatal(err)
}

4️⃣ آپلود فایل

file, err := os.Open("test.jpg")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

stat, _ := file.Stat()

info, err := storage.Upload(
    context.Background(),
    file,
    stat.Size(),
    "test.jpg",
    filestore.UploadOptions{
        ContentType: "image/jpeg",
    },
)
if err != nil {
    log.Fatal(err)
}

fmt.Println("Public ID:", info.ID)

5️⃣ دانلود فایل

reader, meta, err := storage.Download(ctx, publicID)
if err != nil {
    log.Fatal(err)
}
defer reader.Close()

io.Copy(os.Stdout, reader)

6️⃣ حذف فایل

err := storage.Delete(ctx, publicID)
if err != nil {
    log.Fatal(err)
}

7️⃣ تولید Presigned URL

url, err := storage.PresignedURL(ctx, publicID, 15*time.Minute)
if err != nil {
    log.Fatal(err)
}

fmt.Println(url)

8️⃣ لیست فایل‌ها

files, err := storage.List(ctx, filestore.ListOptions{
    Prefix: "images/",
})
if err != nil {
    log.Fatal(err)
}

for _, f := range files {
    fmt.Println(f.ID, f.Name)
}

🔌 اتصال به Gin (اختیاری)

این ماژول مستقل از Gin است، اما می‌توانید adaptor بسازید:
func UploadHandler(storage filestore.Storage) gin.HandlerFunc {
return func(c *gin.Context) {
file, header, err := c.Request.FormFile("file")
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
    }
defer file.Close()

info, err := storage.Upload(
c.Request.Context(),
file,
header.Size,
header.Filename,
filestore.UploadOptions{
ContentType: header.Header.Get("Content-Type"),
    },
)

if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}

c.JSON(201, info)
    }
}

⚡ بهترین شیوه‌های استفاده در Production

  • ✔ همیشه از Context با timeout استفاده کنید
  • ✔ Bucket versioning را فعال کنید
  • ✔ Server-Side Encryption را فعال کنید
  • ✔ اندازه فایل را قبل از آپلود validate کنید
  • ✔ Logging را با request ID enrich کنید
  • ✔ Presigned URL مدت کوتاه داشته باشد

🔄 توسعه در آینده

این ماژول به سادگی قابل گسترش است:
  • پیاده‌سازی S3 Adapter
  • اضافه کردن Metadata persistence
  • اضافه کردن Event-driven publishing
  • اضافه کردن OpenTelemetry tracing
  • پیاده‌سازی Rate limiting در adaptor

📌 جمع‌بندی

ماژول Filestore:
  • 🧱 مستقل از فریم‌ورک
  • 🧩 قابل جایگزینی
  • 🚀 آماده استفاده در Production
  • 🔐 امن
  • ⚙️ بهینه و scalable
  • 🧠 طراحی‌شده با اصول Clean Architecture

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// Validation errors
	ErrInvalidFileSize = errors.New("invalid file size")
	ErrInvalidFileName = errors.New("invalid file name")
	ErrInvalidMimeType = errors.New("invalid mime type")
	ErrFileTooLarge    = errors.New("file exceeds allowed size")

	// Storage errors
	ErrFileNotFound       = errors.New("file not found")
	ErrFileAlreadyExists  = errors.New("file already exists")
	ErrStorageUnavailable = errors.New("storage service unavailable")

	// Security errors
	ErrUnauthorizedAccess = errors.New("unauthorized file access")

	// Versioning
	ErrVersionNotFound = errors.New("file version not found")
)

Functions

func UploadHandler

func UploadHandler(storage Storage) gin.HandlerFunc

Types

type Config

type Config struct {
	Endpoint         string
	AccessKey        string
	SecretKey        string
	UseSSL           bool
	Bucket           string
	EnableVersioning bool
	EnableSSE        bool
}

func LoadFromEnv

func LoadFromEnv() (*Config, error)

type FileInfo

type FileInfo struct {
	ID        string // ULID (public)
	Name      string // original filename
	Size      int64
	MimeType  string
	ETag      string
	VersionID string
	CreatedAt time.Time
}

type ListOptions

type ListOptions struct {
	Prefix string
	Suffix string
}

type Logger

type Logger interface {
	Info(ctx context.Context, msg string, fields map[string]interface{})
	Error(ctx context.Context, msg string, fields map[string]interface{})
}

Logger interface همان است

type Storage

type Storage interface {
	Upload(ctx context.Context, reader io.Reader, size int64, originalName string, opts UploadOptions) (*FileInfo, error)
	Download(ctx context.Context, id string) (io.ReadCloser, *FileInfo, error)
	Delete(ctx context.Context, id string) error
	PresignedURL(ctx context.Context, id string, expiry time.Duration) (string, error)
	List(ctx context.Context, opts ListOptions) ([]FileInfo, error)
}

type UploadOptions

type UploadOptions struct {
	ContentType string
	Metadata    map[string]string
}

type ZapLogger

type ZapLogger struct {
	// contains filtered or unexported fields
}

ZapLogger حرفه‌ای با zap

func NewZapLogger

func NewZapLogger() *ZapLogger

NewZapLogger یک logger حرفه‌ای با JSON output می‌سازد

func (*ZapLogger) Error

func (l *ZapLogger) Error(ctx context.Context, msg string, fields map[string]interface{})

Error با context و فیلدهای دلخواه

func (*ZapLogger) Info

func (l *ZapLogger) Info(ctx context.Context, msg string, fields map[string]interface{})

Info با context و فیلدهای دلخواه

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL