feat: add file upload workflow
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"mime/multipart"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDependencyUnavailable = errors.New("dependency unavailable")
|
||||
ErrInvalidFile = errors.New("invalid file")
|
||||
)
|
||||
|
||||
const maxUploadSize = 10 * 1024 * 1024
|
||||
|
||||
var allowedContentTypes = map[string]bool{
|
||||
"image/jpeg": true,
|
||||
"image/png": true,
|
||||
"image/webp": true,
|
||||
"application/pdf": true,
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
storage *Storage
|
||||
}
|
||||
|
||||
func NewService(storage *Storage) *Service {
|
||||
return &Service{storage: storage}
|
||||
}
|
||||
|
||||
func (s *Service) Upload(req uploadRequest) (*UploadDTO, error) {
|
||||
if s.storage == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if req.Header == nil || req.Reader == nil || req.Header.Size <= 0 || req.Header.Size > maxUploadSize {
|
||||
return nil, ErrInvalidFile
|
||||
}
|
||||
contentType := req.ContentType
|
||||
if contentType == "" {
|
||||
contentType = req.Header.Header.Get("Content-Type")
|
||||
}
|
||||
if !allowedContentTypes[contentType] {
|
||||
return nil, ErrInvalidFile
|
||||
}
|
||||
scene := normalizeScene(req.Scene)
|
||||
key, err := s.storage.Put(req.Context, scene, req.Header, req.Reader, contentType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UploadDTO{
|
||||
ObjectKey: key,
|
||||
URL: "/api/files/object?key=" + key,
|
||||
Filename: req.Header.Filename,
|
||||
ContentType: contentType,
|
||||
Size: req.Header.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func normalizeScene(scene string) string {
|
||||
scene = strings.TrimSpace(strings.ToLower(scene))
|
||||
switch scene {
|
||||
case "listing", "handoff", "dispute", "realname", "avatar":
|
||||
return scene
|
||||
default:
|
||||
return "misc"
|
||||
}
|
||||
}
|
||||
|
||||
type uploadRequest struct {
|
||||
Context context.Context
|
||||
Scene string
|
||||
Header *multipart.FileHeader
|
||||
Reader multipart.File
|
||||
ContentType string
|
||||
}
|
||||
Reference in New Issue
Block a user