StorageServer/modules/image/image.go

175 lines
3.7 KiB
Go

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
}