package handlers import ( "encoding/base64" "errors" "io" "net/http" "strings" "github.com/gofiber/fiber/v2" ) const ( maxAvatarImageBytes = 2 << 20 maxBackgroundImageBytes = 6 << 20 ) var ( errImageTooLarge = errors.New("image is too large") errImageType = errors.New("unsupported image type") ) func validImageDataURI(value string) bool { for _, prefix := range []string{ "data:image/png;base64,", "data:image/jpeg;base64,", "data:image/webp;base64,", "data:image/gif;base64,", } { if strings.HasPrefix(value, prefix) { return true } } return false } // readImageField accepts either a browser-resized data URI or a multipart // upload for one image field. An empty result means the field was not changed. func readImageField(c *fiber.Ctx, dataField, fileField string, maxBytes int64) (string, error) { if data := strings.TrimSpace(c.FormValue(dataField)); data != "" { if !validImageDataURI(data) { return "", errImageType } if int64(len(data)) > maxBytes*4/3+4096 { return "", errImageTooLarge } return data, nil } file, err := c.FormFile(fileField) if err != nil || file == nil || file.Size <= 0 { return "", nil } if file.Size > maxBytes { return "", errImageTooLarge } f, err := file.Open() if err != nil { return "", err } defer f.Close() buf, err := io.ReadAll(io.LimitReader(f, maxBytes+1)) if err != nil { return "", err } if int64(len(buf)) > maxBytes { return "", errImageTooLarge } mime := http.DetectContentType(buf) if semi := strings.IndexByte(mime, ';'); semi >= 0 { mime = mime[:semi] } switch mime { case "image/png", "image/jpeg", "image/gif", "image/webp": default: return "", errImageType } return "data:" + mime + ";base64," + base64.StdEncoding.EncodeToString(buf), nil }