82 lines
1.9 KiB
Go
82 lines
1.9 KiB
Go
package file
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"mime/multipart"
|
|
"net/url"
|
|
"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")
|
|
}
|
|
scene := normalizeScene(req.Scene)
|
|
if !allowedContentTypes[contentType] || (scene == "chat" && !strings.HasPrefix(contentType, "image/")) {
|
|
return nil, ErrInvalidFile
|
|
}
|
|
key, err := s.storage.Put(req.Context, scene, req.Header, req.Reader, contentType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fileURL := "/api/files/object?key=" + url.QueryEscape(key)
|
|
if scene == "home-banner" || scene == "avatar" {
|
|
fileURL = "/api/public/files/object?key=" + url.QueryEscape(key)
|
|
}
|
|
return &UploadDTO{
|
|
ObjectKey: key,
|
|
URL: fileURL,
|
|
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", "home-banner", "chat":
|
|
return scene
|
|
default:
|
|
return "misc"
|
|
}
|
|
}
|
|
|
|
type uploadRequest struct {
|
|
Context context.Context
|
|
Scene string
|
|
Header *multipart.FileHeader
|
|
Reader multipart.File
|
|
ContentType string
|
|
}
|