172 lines
3.6 KiB
Go
172 lines
3.6 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
|
|
|
|
if fileHeader.Header.Get("Content-Type") != "image/png" {
|
|
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
|
|
}
|