mirror of
https://github.com/keven1024/015.git
synced 2026-05-26 07:08:02 +00:00
feat(services): implement image compression functionality for PNG and JPEG formats with corresponding unit tests
This commit is contained in:
34
worker/internal/services/image.go
Normal file
34
worker/internal/services/image.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
13
worker/internal/tasks/types.go
Normal file
13
worker/internal/tasks/types.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package tasks
|
||||
|
||||
type BaseFileTaskPayload struct {
|
||||
FileId string `json:"file_id"`
|
||||
}
|
||||
|
||||
type RemoveFileTaskPayload struct {
|
||||
BaseFileTaskPayload
|
||||
}
|
||||
|
||||
type CompressImageTaskPayload struct {
|
||||
BaseFileTaskPayload
|
||||
}
|
||||
BIN
worker/test/resource/test.jpg
Normal file
BIN
worker/test/resource/test.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
worker/test/resource/test.png
Normal file
BIN
worker/test/resource/test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
2
worker/test/resource/test.svg
Normal file
2
worker/test/resource/test.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 110 KiB |
66
worker/test/services/image_test.go
Normal file
66
worker/test/services/image_test.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user