package user import ( "bytes" "fmt" "image" _ "image/jpeg" _ "image/png" "io" "net/http" "path/filepath" "strings" ) const ( MaxWidth = 512 MaxHeight = 512 MaxUploadSize = 5 * 1024 * 1024 ) func VerifyImageLimit(r io.Reader) error { cfg, format, err := image.DecodeConfig(r) if err != nil { return fmt.Errorf("failed to decode image: %w", err) } switch format { case "jpeg", "png": // Allowed formats default: return fmt.Errorf("not supported format: %s", format) } if cfg.Width > MaxWidth || cfg.Height > MaxHeight { return fmt.Errorf("image dimensions exceed limits: %dx%d (max %dx%d)", cfg.Width, cfg.Height, MaxWidth, MaxHeight) } return nil } func ExtractUploadedImageBytes(r *http.Request, formKey string) ([]byte, string, error) { // Read file with a size limit to prevent abuse. We read the raw multipart // bytes, verify size and image format, then return them. const maxRead = MaxUploadSize + 1024 // small extra for multipart framing file, header, err := r.FormFile(formKey) if err != nil { return nil, "", fmt.Errorf("form error: %w", err) } defer file.Close() ext := strings.ToLower(filepath.Ext(header.Filename)) if ext != ".jpg" && ext != ".jpeg" && ext != ".png" { return nil, "", fmt.Errorf("invalid file type: only JPG or PNG are allowed") } // Read all bytes so we can verify image dimensions from a reusable reader. lr := io.LimitReader(file, maxRead) fileBytes, err := io.ReadAll(lr) if err != nil { return nil, "", err } if int64(len(fileBytes)) >= maxRead { return nil, "", fmt.Errorf("file too large (max %d bytes)", MaxUploadSize) } if err := VerifyImageLimit(bytes.NewReader(fileBytes)); err != nil { return nil, "", fmt.Errorf("invalid file: %w", err) } return fileBytes, header.Filename, nil }