upload.go 2.3 KB

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