Skip to content

Commit

Permalink
perf: 将上传的图片转换为WebP
Browse files Browse the repository at this point in the history
  • Loading branch information
SugarMGP committed Dec 4, 2024
1 parent bce3d25 commit d389b43
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 47 deletions.
34 changes: 17 additions & 17 deletions app/controllers/objectController/upload.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package objectController

import (
"bytes"
"errors"
"io"
"mime/multipart"

"4u-go/app/apiException"
Expand All @@ -26,22 +26,15 @@ func UploadFile(c *gin.Context) {
}

uploadType := data.UploadType
fileHeader := data.File
// 获取文件流
file, err := data.File.Open()
fileSize := data.File.Size
fileData, err := objectService.ReadFileToBytes(data.File)
if err != nil {
apiException.AbortWithException(c, apiException.ServerError, err)
apiException.AbortWithException(c, apiException.UploadFileError, err)
return
}
defer func(file multipart.File) {
err := file.Close()
if err != nil {
zap.L().Error("文件流关闭错误", zap.Error(err))
}
}(file)

// 获取文件信息
contentType, fileExt, err := objectService.GetFileInfo(file, fileHeader, uploadType)
contentType, fileExt, err := objectService.GetFileInfo(fileData, fileSize, uploadType)
if errors.Is(err, objectService.ErrSizeExceeded) {
apiException.AbortWithException(c, apiException.FileSizeExceedError, err)
return
Expand All @@ -58,15 +51,22 @@ func UploadFile(c *gin.Context) {
apiException.AbortWithException(c, apiException.ServerError, err)
return
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
apiException.AbortWithException(c, apiException.ServerError, err)
return

if uploadType == objectService.TypeImage {
d, s, err := objectService.ConvertToWebP(fileData)
if err != nil {
zap.L().Error("转换图片到 WebP 失败", zap.Error(err))
} else { // 若转换成功则替代原文件
fileData = d
fileSize = s
fileExt = ".webp"
contentType = "image/webp"
}
}

// 上传文件
objectKey := objectService.GenerateObjectKey(uploadType, fileExt)
objectUrl, err := objectService.PutObject(objectKey, file, fileHeader.Size, contentType)
objectUrl, err := objectService.PutObject(objectKey, bytes.NewReader(fileData), fileSize, contentType)
if err != nil {
apiException.AbortWithException(c, apiException.UploadFileError, err)
return
Expand Down
70 changes: 56 additions & 14 deletions app/services/objectService/objectService.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package objectService

import (
"bytes"
"errors"
"fmt"
"io"
"mime/multipart"
"strings"
"time"

"github.com/chai2010/webp"
"github.com/disintegration/imaging"
"github.com/dustin/go-humanize"
"github.com/gabriel-vasile/mimetype"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
)

var (
Expand All @@ -23,34 +28,39 @@ var (
ErrNotImage = errors.New("file isn't a image")
)

const (
// TypeImage 图片
TypeImage = "image"

// TypeAttachment 附件
TypeAttachment = "attachment"
)

var uploadTypeLimits = map[string]int64{
"public/image": humanize.MByte * 10,
"public/attachment": humanize.MByte * 100,
TypeImage: humanize.MByte * 10,
TypeAttachment: humanize.MByte * 100,
}

// GetFileInfo 获取文件基本信息
func GetFileInfo(
file multipart.File,
fileHeader *multipart.FileHeader,
fileData []byte,
fileSize int64,
uploadType string,
) (
contentType string,
fileExt string,
err error,
) {
// 检查文件大小
if err = checkFileSize(uploadType, fileHeader.Size); err != nil {
if err = checkFileSize(uploadType, fileSize); err != nil {
return "", "", err
}

// 通过文件头获取类型和扩展名
mimeType, mimeExt, err := getFileTypeAndExt(file)
if err != nil {
return "", "", err
}
mimeType, mimeExt := getFileTypeAndExt(fileData)

// 检查是否为图像类型
if uploadType == "public/image" && !strings.HasPrefix(mimeType, "image") {
if uploadType == TypeImage && !strings.HasPrefix(mimeType, "image") {
return "", "", ErrNotImage
}

Expand All @@ -75,10 +85,42 @@ func checkFileSize(uploadType string, size int64) error {
}

// getFileTypeAndExt 根据文件头(Magic Number)判断文件类型和扩展名
func getFileTypeAndExt(file multipart.File) (mimeType string, mimeExt string, err error) {
mime, err := mimetype.DetectReader(file)
func getFileTypeAndExt(fileData []byte) (mimeType string, mimeExt string) {
mime := mimetype.Detect(fileData)
return mime.String(), mime.Extension()
}

// ConvertToWebP 将图片转换为 WebP 格式
func ConvertToWebP(fileData []byte) ([]byte, int64, error) {
img, err := imaging.Decode(bytes.NewReader(fileData))
if err != nil {
return "", "", err
return nil, 0, err
}

var buf bytes.Buffer
err = webp.Encode(&buf, img, &webp.Options{Quality: 100})
if err != nil {
return nil, 0, err
}
return buf.Bytes(), int64(buf.Len()), nil
}

// ReadFileToBytes 读取文件数据
func ReadFileToBytes(fileHeader *multipart.FileHeader) ([]byte, error) {
file, err := fileHeader.Open()
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer func(file multipart.File) {
err := file.Close()
if err != nil {
zap.L().Warn("文件关闭失败", zap.Error(err))
}
}(file)

data, err := io.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
return mime.String(), mime.Extension(), nil
return data, nil
}
19 changes: 6 additions & 13 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@

### 如何参与开发

1. 克隆该项目并切换到dev分支,然后切出自己的分支进行开发
1. 安装`GCC``MinGW`并配置好环境([下载](http://tdm-gcc.tdragon.net/download)

2. 克隆该项目并切换到`dev`分支,然后切出自己的分支进行开发

```shell
git clone https://github.com/zjutjh/4UOnline-Go.git
Expand All @@ -58,7 +60,7 @@ git reset --hard origin/dev
git push --force
```

2. 复制示例配置,并按注释要求填写配置文件(`user`配置询问部长团,并要提供个人学号)
3. 复制示例配置,并按注释要求填写配置文件(`user`配置询问部长团,并要提供个人学号)

```shell
/* Linux */
Expand All @@ -68,25 +70,16 @@ cp config.example.yaml config.yaml
copy config.example.yaml config.yaml
```

3. 启动程序
4. 启动程序

```shell
go run main.go
```

4. 每次提交 commit 前,先运行以下命令来格式化代码并检查规范(需要安装 [gci](https://github.com/daixiang0/gci)[golangci-lint](https://golangci-lint.run/)
5. 每次提交 commit 前,先运行以下命令来格式化代码并检查规范(需要安装 [gci](https://github.com/daixiang0/gci)[golangci-lint](https://golangci-lint.run/)

```
gofmt -w .
gci write . -s standard -s default
golangci-lint run --config .golangci.yml
```

5. 打包后端到服务器运行

```
SET CGO_ENABLE=0
SET GOOS=linux
SET GOARCH=amd64
go build -o 4u main.go
```
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module 4u-go
go 1.22.9

require (
github.com/chai2010/webp v1.1.1
github.com/disintegration/imaging v1.6.2
github.com/dustin/go-humanize v1.0.1
github.com/gabriel-vasile/mimetype v1.4.6
github.com/gin-contrib/cors v1.7.2
Expand All @@ -12,7 +14,6 @@ require (
github.com/go-resty/resty/v2 v2.16.0
github.com/json-iterator/go v1.1.12
github.com/minio/minio-go/v7 v7.0.80
github.com/pkg/errors v0.9.1
github.com/satori/go.uuid v1.2.0
github.com/silenceper/wechat/v2 v2.1.7
github.com/spf13/viper v1.19.0
Expand Down Expand Up @@ -77,6 +78,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.12.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/image v0.22.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
Expand Down
9 changes: 7 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
Expand All @@ -30,6 +32,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
Expand Down Expand Up @@ -159,8 +163,6 @@ github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -235,6 +237,9 @@ golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down

0 comments on commit d389b43

Please sign in to comment.