package upload import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "os" "path/filepath" "strings" "github.com/google/uuid" ) var allowedTypes = map[string]string{ "image/jpeg": ".jpg", "image/png": ".png", "image/webp": ".webp", "image/gif": ".gif", } type Storage struct { RootDir string } func NewStorage(root string) (*Storage, error) { if err := os.MkdirAll(root, 0755); err != nil { return nil, err } return &Storage{RootDir: root}, nil } func (s *Storage) SaveProductImage(productID int, file multipart.File, header *multipart.FileHeader) (string, error) { buf := make([]byte, 512) n, err := file.Read(buf) if err != nil && err != io.EOF { return "", err } contentType := http.DetectContentType(buf[:n]) ext, ok := allowedTypes[contentType] if !ok { return "", fmt.Errorf("недопустимый тип файла: %s", contentType) } if _, err := file.Seek(0, io.SeekStart); err != nil { return "", err } dir := filepath.Join(s.RootDir, "products", fmt.Sprintf("%d", productID)) if err := os.MkdirAll(dir, 0755); err != nil { return "", err } name := uuid.New().String() + ext fullPath := filepath.Join(dir, name) dst, err := os.Create(fullPath) if err != nil { return "", err } defer dst.Close() if _, err := io.Copy(dst, io.MultiReader(bytes.NewReader(buf[:n]), file)); err != nil { os.Remove(fullPath) return "", err } webPath := "/uploads/products/" + fmt.Sprintf("%d", productID) + "/" + name _ = header return webPath, nil } func (s *Storage) Remove(webPath string) error { if !strings.HasPrefix(webPath, "/uploads/") { return nil } rel := strings.TrimPrefix(webPath, "/uploads/") full := filepath.Join(s.RootDir, rel) return os.Remove(full) } func (s *Storage) FileServer() http.Handler { return http.StripPrefix("/uploads/", http.FileServer(http.Dir(s.RootDir))) }