package image import ( "errors" "io" "mime/multipart" "os" "strconv" "clickandjoin.app/storageserver/modules/utils" "github.com/h2non/bimg" "github.com/sirupsen/logrus" ) // is specified in the form data of the frontend const FormFileKey = "file" const MaxAvatarSize = 2 * 1024 * 1024 // 2 MB // filename len = 36 + dot . 1 + longestFileType 4 // example: cf75ace7-da4c-434d-bf7d-0bdbcea5c57d.webp const MaxFileNameLen = 41 var validFileTypes = []string{"jpeg", "jpg", "png", "webp"} // represents the query parameter size for the dynamic image resolution // example ?size=1 -> 64x64 var ImageSizes = []int{32, 64, 150, 256, 512, 1024} const DefaultImageSize = 4 func FileTypeVerification(fileType string) bool { for _, validFileType := range validFileTypes { if fileType == "image/"+validFileType { return true } } return false } func createUserFolder(userId string) error { path := utils.GetUserStoragePath(userId) if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { err = os.Mkdir(path, os.ModePerm) if err != nil && !errors.Is(err, os.ErrExist) { logrus.Errorln("Failed to create user folder, err:", err) return err } } return nil } func SaveImage(fileHeader *multipart.FileHeader, userId string, imagePath string, imageWidth int, imageHeight int, imageQuality int) (err error) { err = createUserFolder(userId) if err != nil { return err } f, err := fileHeader.Open() if err != nil { logrus.Errorln("Failed to open file header, err:", err) return err } fileData, err := io.ReadAll(f) if err != nil { logrus.Errorln("Failed to read file, err:", err) return err } // prevent images that have a small resolution from being made larger by the size specified size, err := bimg.NewImage(fileData).Size() if err != nil { logrus.Errorln("Failed to get image size, err:", err) return err } if size.Width < imageWidth { imageWidth = size.Width } if size.Height < imageHeight { imageHeight = size.Height } newImage := fileData logrus.Println("content-type", fileHeader.Header.Get("Content-Type")) if fileHeader.Header.Get("Content-Type") != "image/png" { logrus.Println("here") newImage, err = bimg.NewImage(fileData).Convert(bimg.JPEG) if err != nil { logrus.Errorln("Failed to convert image to png, err:", err) return err } } newImage, err = bimg.NewImage(newImage).Process(bimg.Options{ Width: imageWidth, Height: imageHeight, Quality: imageQuality, }) if err != nil { logrus.Errorln("Failed to process image, err:", err) return err } err = bimg.Write(imagePath+".webp", newImage) if err != nil { logrus.Errorln("Failed to save image, err:", err) return err } return nil } func GetImage(userId string, fileName string, imageSize string) ([]byte, error) { imgSize, err := strconv.Atoi(imageSize) if err != nil { imgSize = DefaultImageSize } buffer, err := bimg.Read(utils.GetUserStoragePath(userId) + fileName) if err != nil { logrus.Errorln("Failed to read image, err:", err) return []byte{}, err } // check that the value has not been manipulated and is greater than the largest index value in the list if imgSize > len(ImageSizes)-1 { imgSize = DefaultImageSize } resolution := ImageSizes[imgSize] newImage, err := bimg.NewImage(buffer).ForceResize(resolution, resolution) if err != nil { logrus.Errorln("Failed to force resize, err:", err) return []byte{}, err } return newImage, nil } func DeleteOldAvatarImage(userId string) error { path := utils.GetUserStoragePath(userId) dir, err := os.ReadDir(path) if err != nil && !errors.Is(err, os.ErrNotExist) { logrus.Errorln("Failed to read dir, err:", err) return err } for _, file := range dir { os.Remove(path + file.Name()) } return nil }