144 lines
3.6 KiB
Go
144 lines
3.6 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"fmt"
|
|
"mime/multipart"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/minio/minio-go/v7"
|
|
)
|
|
|
|
type fileUploadService struct {
|
|
minioClient *minio.Client
|
|
bucketName string
|
|
}
|
|
|
|
type FileUploadService interface {
|
|
UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error)
|
|
DeleteFile(filePath string) error
|
|
GetFileURL(filePath string) (string, error)
|
|
ValidateFile(file *multipart.FileHeader) error
|
|
}
|
|
|
|
func NewFileUploadService(minioClient *minio.Client, bucketName string) FileUploadService {
|
|
return &fileUploadService{
|
|
minioClient: minioClient,
|
|
bucketName: bucketName,
|
|
}
|
|
}
|
|
|
|
// UploadFile - Upload file to MinIO
|
|
func (f *fileUploadService) UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error) {
|
|
// Validate file
|
|
if err := f.ValidateFile(file); err != nil {
|
|
return "", 0, err
|
|
}
|
|
|
|
// Open file
|
|
src, err := file.Open()
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
defer src.Close()
|
|
|
|
// Generate unique filename
|
|
ext := filepath.Ext(file.Filename)
|
|
fileName := strings.TrimSuffix(file.Filename, ext)
|
|
hasher := md5.New()
|
|
hasher.Write([]byte(fmt.Sprintf("%s-%d", fileName, time.Now().UnixNano())))
|
|
uniqueFileName := fmt.Sprintf("%x%s", hasher.Sum(nil), ext)
|
|
|
|
// Create file path
|
|
filePath = fmt.Sprintf("%s/%s", folder, uniqueFileName)
|
|
|
|
// Upload file to MinIO
|
|
ctx := context.Background()
|
|
_, err = f.minioClient.PutObject(ctx, f.bucketName, filePath, src, file.Size, minio.PutObjectOptions{
|
|
ContentType: file.Header.Get("Content-Type"),
|
|
})
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
|
|
return filePath, file.Size, nil
|
|
}
|
|
|
|
// DeleteFile - Delete file from MinIO
|
|
func (f *fileUploadService) DeleteFile(filePath string) error {
|
|
ctx := context.Background()
|
|
return f.minioClient.RemoveObject(ctx, f.bucketName, filePath, minio.RemoveObjectOptions{})
|
|
}
|
|
|
|
// GetFileURL - Get file URL from MinIO
|
|
func (f *fileUploadService) GetFileURL(filePath string) (string, error) {
|
|
ctx := context.Background()
|
|
|
|
// Generate presigned URL (valid for 7 days)
|
|
url, err := f.minioClient.PresignedGetObject(ctx, f.bucketName, filePath, 7*24*time.Hour, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return url.String(), nil
|
|
}
|
|
|
|
// ValidateFile - Validate uploaded file
|
|
func (f *fileUploadService) ValidateFile(file *multipart.FileHeader) error {
|
|
// Check file size (max 50MB)
|
|
const maxFileSize = 50 * 1024 * 1024 // 50MB
|
|
if file.Size > maxFileSize {
|
|
return fmt.Errorf("file size exceeds maximum limit of 50MB")
|
|
}
|
|
|
|
// Check file extension
|
|
ext := strings.ToLower(filepath.Ext(file.Filename))
|
|
allowedExts := []string{".pdf", ".doc", ".docx", ".txt", ".mp4", ".avi", ".mov", ".mp3", ".wav", ".jpg", ".jpeg", ".png", ".gif"}
|
|
|
|
isAllowed := false
|
|
for _, allowedExt := range allowedExts {
|
|
if ext == allowedExt {
|
|
isAllowed = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !isAllowed {
|
|
return fmt.Errorf("file type not allowed. Allowed types: %s", strings.Join(allowedExts, ", "))
|
|
}
|
|
|
|
// Check MIME type
|
|
contentType := file.Header.Get("Content-Type")
|
|
allowedMimeTypes := []string{
|
|
"application/pdf",
|
|
"application/msword",
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
"text/plain",
|
|
"video/mp4",
|
|
"video/avi",
|
|
"video/quicktime",
|
|
"audio/mpeg",
|
|
"audio/wav",
|
|
"image/jpeg",
|
|
"image/png",
|
|
"image/gif",
|
|
}
|
|
|
|
isValidMimeType := false
|
|
for _, allowedMimeType := range allowedMimeTypes {
|
|
if contentType == allowedMimeType {
|
|
isValidMimeType = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !isValidMimeType {
|
|
return fmt.Errorf("invalid file type. Content-Type: %s", contentType)
|
|
}
|
|
|
|
return nil
|
|
}
|