upload.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package handlers
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "time"
  6. "github.com/google/uuid"
  7. "github.com/minio/minio-go/v7"
  8. "github.com/minio/minio-go/v7/pkg/credentials"
  9. )
  10. type UploadHandler struct {
  11. minioClient *minio.Client
  12. bucket string
  13. endpoint string
  14. publicEndpoint string
  15. }
  16. func NewUploadHandler(endpoint, publicEndpoint, accessKey, secretKey, bucket string, useSSL bool) (*UploadHandler, error) {
  17. client, err := minio.New(endpoint, &minio.Options{
  18. Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
  19. Secure: useSSL,
  20. })
  21. if err != nil {
  22. return nil, err
  23. }
  24. return &UploadHandler{
  25. minioClient: client,
  26. bucket: bucket,
  27. endpoint: endpoint,
  28. publicEndpoint: publicEndpoint,
  29. }, nil
  30. }
  31. type presignedURLRequest struct {
  32. ContentType string `json:"content_type"`
  33. MaxSizeMB int `json:"max_size_mb"`
  34. }
  35. type presignedURLResponse struct {
  36. UploadURL string `json:"upload_url"`
  37. FileURL string `json:"file_url"`
  38. Fields map[string]string `json:"fields"`
  39. }
  40. func (h *UploadHandler) PresignedURL(w http.ResponseWriter, r *http.Request) {
  41. var req presignedURLRequest
  42. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  43. writeError(w, http.StatusBadRequest, "invalid request body")
  44. return
  45. }
  46. if req.MaxSizeMB <= 0 || req.MaxSizeMB > 50 {
  47. req.MaxSizeMB = 10
  48. }
  49. validContentTypes := map[string]bool{
  50. "image/jpeg": true, "image/png": true, "image/webp": true, "image/heic": true,
  51. }
  52. if !validContentTypes[req.ContentType] {
  53. writeError(w, http.StatusBadRequest, "unsupported content type")
  54. return
  55. }
  56. fileID := uuid.New().String()
  57. ext := ".jpg"
  58. switch req.ContentType {
  59. case "image/png":
  60. ext = ".png"
  61. case "image/webp":
  62. ext = ".webp"
  63. case "image/heic":
  64. ext = ".heic"
  65. }
  66. objectName := "uploads/" + fileID + ext
  67. policy := minio.NewPostPolicy()
  68. policy.SetBucket(h.bucket)
  69. policy.SetKey(objectName)
  70. policy.SetExpires(time.Now().Add(15 * time.Minute))
  71. policy.SetContentType(req.ContentType)
  72. policy.SetContentLengthRange(1, int64(req.MaxSizeMB)*1024*1024)
  73. url, formData, err := h.minioClient.PresignedPostPolicy(r.Context(), policy)
  74. if err != nil {
  75. writeError(w, http.StatusInternalServerError, "failed to generate upload URL")
  76. return
  77. }
  78. fileURL := h.publicEndpoint + "/" + h.bucket + "/" + objectName
  79. writeJSON(w, http.StatusOK, presignedURLResponse{
  80. UploadURL: url.String(),
  81. FileURL: fileURL,
  82. Fields: formData,
  83. })
  84. }