feat(services): implement image compression functionality for PNG and JPEG formats with corresponding unit tests

This commit is contained in:
keven1024
2026-02-25 12:04:22 +08:00
parent 301dcaf8b6
commit 155c697e53
8 changed files with 118 additions and 37 deletions

View File

@@ -0,0 +1,34 @@
package services
import (
"errors"
"os/exec"
"worker/internal/utils"
)
func CompressImage(filePath string, mimeType string) (string, error) {
compressedPath := filePath + "_compressed"
switch mimeType {
case "image/png":
args := []string{"--output", compressedPath, filePath}
cmd := exec.Command("pngquant", args...)
_, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
case "image/jpeg":
err := utils.CopyFile(filePath, compressedPath)
if err != nil {
return "", err
}
args := []string{"-m", "90", "--strip-all", compressedPath}
cmd := exec.Command("jpegoptim", args...)
_, err = cmd.CombinedOutput()
if err != nil {
return "", err
}
default:
return "", errors.New("不支持的文件类型")
}
return compressedPath, nil
}

View File

@@ -12,12 +12,7 @@ import (
"github.com/hibiken/asynq"
)
type RemoveFileTaskPayload struct {
FileId string `json:"file_id"`
}
func RemoveFile(ctx context.Context, task *asynq.Task) error {
var payload RemoveFileTaskPayload
if err := json.Unmarshal(task.Payload(), &payload); err != nil {
return err

View File

@@ -4,8 +4,6 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"os/exec"
"path/filepath"
"pkg/models"
"worker/internal/services"
@@ -14,14 +12,6 @@ import (
"github.com/hibiken/asynq"
)
type ImageTaskPayload struct {
FileId string `json:"file_id"`
}
type CompressImageTaskPayload struct {
ImageTaskPayload
}
func CompressImage(ctx context.Context, task *asynq.Task) error {
var payload CompressImageTaskPayload
if err := json.Unmarshal(task.Payload(), &payload); err != nil {
@@ -36,29 +26,10 @@ func CompressImage(ctx context.Context, task *asynq.Task) error {
return err
}
originalPath := filepath.Join(uploadPath, payload.FileId)
switch originalFileInfo.MimeType {
case "image/png":
args := []string{"--output", originalPath + "_compressed", originalPath}
cmd := exec.Command("pngquant", args...)
_, err := cmd.CombinedOutput()
if err != nil {
return err
}
case "image/jpeg":
err := utils.CopyFile(originalPath, originalPath+"_compressed")
if err != nil {
return err
}
args := []string{"-m", "90", "--strip-all", originalPath + "_compressed"}
cmd := exec.Command("jpegoptim", args...)
_, err = cmd.CombinedOutput()
if err != nil {
return err
}
default:
return errors.New("不支持的文件类型")
compressedPath, err := services.CompressImage(originalPath, originalFileInfo.MimeType)
if err != nil {
return err
}
compressedPath := fmt.Sprintf("%s_compressed", originalPath)
compressedFileInfo, err := services.GenStandardFile(compressedPath, originalFileInfo.MimeType)
if err != nil {
return err

View File

@@ -0,0 +1,13 @@
package tasks
type BaseFileTaskPayload struct {
FileId string `json:"file_id"`
}
type RemoveFileTaskPayload struct {
BaseFileTaskPayload
}
type CompressImageTaskPayload struct {
BaseFileTaskPayload
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -0,0 +1,66 @@
package services
import (
"fmt"
"os"
"path/filepath"
"runtime"
"testing"
"worker/internal/services"
"worker/internal/utils"
"github.com/stretchr/testify/assert"
)
func TestCompressPNGHappyPath(t *testing.T) {
tmp := t.TempDir()
filePath := filepath.Join(tmp, "test.png")
// 从 test/resource 复制真实 PNG路径基于当前测试文件位置
_, self, _, _ := runtime.Caller(0)
srcPath := filepath.Join(filepath.Dir(self), "..", "resource", "test.png")
err := utils.CopyFile(srcPath, filePath)
if err != nil {
t.Fatal(err)
}
got, err := services.CompressImage(filePath, "image/png")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, got, filePath+"_compressed")
origInfo, err := os.Stat(filePath)
if err != nil {
t.Fatal(err)
}
compInfo, err := os.Stat(got)
if err != nil {
t.Fatal(err)
}
assert.NotEqual(t, origInfo.Size(), compInfo.Size())
fmt.Printf("原图: %d | 压缩后: %d | 压缩率: %f%%\n", origInfo.Size(), compInfo.Size(), float64(origInfo.Size())/float64(compInfo.Size())*100)
}
func TestCompressJPEGHappyPath(t *testing.T) {
tmp := t.TempDir()
filePath := filepath.Join(tmp, "test.jpg")
_, self, _, _ := runtime.Caller(0)
srcPath := filepath.Join(filepath.Dir(self), "..", "resource", "test.jpg")
err := utils.CopyFile(srcPath, filePath)
if err != nil {
t.Fatal(err)
}
got, err := services.CompressImage(filePath, "image/jpeg")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, got, filePath+"_compressed")
origInfo, err := os.Stat(filePath)
if err != nil {
t.Fatal(err)
}
compInfo, err := os.Stat(got)
if err != nil {
t.Fatal(err)
}
assert.NotEqual(t, origInfo.Size(), compInfo.Size())
fmt.Printf("原图: %d | 压缩后: %d | 压缩率: %f%%\n", origInfo.Size(), compInfo.Size(), float64(origInfo.Size())/float64(compInfo.Size())*100)
}