This commit refactors the logging mechanism across the application by replacing direct logger calls with a centralized logging approach using the `common` package. Key changes include: - Replaced instances of `logger.SysLog` and `logger.FatalLog` with `common.SysLog` and `common.FatalLog` for consistent logging practices. - Updated resource initialization error handling to utilize the new logging structure, enhancing maintainability and readability. - Minor adjustments to improve code clarity and organization throughout various modules. This change aims to streamline logging and improve the overall architecture of the codebase.
173 lines
5.2 KiB
Go
173 lines
5.2 KiB
Go
package service
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/base64"
|
||
"errors"
|
||
"fmt"
|
||
"image"
|
||
"io"
|
||
"net/http"
|
||
"one-api/common"
|
||
"one-api/constant"
|
||
"strings"
|
||
|
||
"golang.org/x/image/webp"
|
||
)
|
||
|
||
func DecodeBase64ImageData(base64String string) (image.Config, string, string, error) {
|
||
// 去除base64数据的URL前缀(如果有)
|
||
if idx := strings.Index(base64String, ","); idx != -1 {
|
||
base64String = base64String[idx+1:]
|
||
}
|
||
|
||
// 将base64字符串解码为字节切片
|
||
decodedData, err := base64.StdEncoding.DecodeString(base64String)
|
||
if err != nil {
|
||
fmt.Println("Error: Failed to decode base64 string")
|
||
return image.Config{}, "", "", fmt.Errorf("failed to decode base64 string: %s", err.Error())
|
||
}
|
||
|
||
// 创建一个bytes.Buffer用于存储解码后的数据
|
||
reader := bytes.NewReader(decodedData)
|
||
config, format, err := getImageConfig(reader)
|
||
return config, format, base64String, err
|
||
}
|
||
|
||
func DecodeBase64FileData(base64String string) (string, string, error) {
|
||
var mimeType string
|
||
var idx int
|
||
idx = strings.Index(base64String, ",")
|
||
if idx == -1 {
|
||
_, file_type, base64, err := DecodeBase64ImageData(base64String)
|
||
return "image/" + file_type, base64, err
|
||
}
|
||
mimeType = base64String[:idx]
|
||
base64String = base64String[idx+1:]
|
||
idx = strings.Index(mimeType, ";")
|
||
if idx == -1 {
|
||
_, file_type, base64, err := DecodeBase64ImageData(base64String)
|
||
return "image/" + file_type, base64, err
|
||
}
|
||
mimeType = mimeType[:idx]
|
||
idx = strings.Index(mimeType, ":")
|
||
if idx == -1 {
|
||
_, file_type, base64, err := DecodeBase64ImageData(base64String)
|
||
return "image/" + file_type, base64, err
|
||
}
|
||
mimeType = mimeType[idx+1:]
|
||
return mimeType, base64String, nil
|
||
}
|
||
|
||
// GetImageFromUrl 获取图片的类型和base64编码的数据
|
||
func GetImageFromUrl(url string) (mimeType string, data string, err error) {
|
||
resp, err := DoDownloadRequest(url)
|
||
if err != nil {
|
||
return "", "", fmt.Errorf("failed to download image: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// Check HTTP status code
|
||
if resp.StatusCode != http.StatusOK {
|
||
return "", "", fmt.Errorf("failed to download image: HTTP %d", resp.StatusCode)
|
||
}
|
||
|
||
contentType := resp.Header.Get("Content-Type")
|
||
if contentType != "application/octet-stream" && !strings.HasPrefix(contentType, "image/") {
|
||
return "", "", fmt.Errorf("invalid content type: %s, required image/*", contentType)
|
||
}
|
||
maxImageSize := int64(constant.MaxFileDownloadMB * 1024 * 1024)
|
||
|
||
// Check Content-Length if available
|
||
if resp.ContentLength > maxImageSize {
|
||
return "", "", fmt.Errorf("image size %d exceeds maximum allowed size of %d bytes", resp.ContentLength, maxImageSize)
|
||
}
|
||
|
||
// Use LimitReader to prevent reading oversized images
|
||
limitReader := io.LimitReader(resp.Body, maxImageSize)
|
||
buffer := &bytes.Buffer{}
|
||
|
||
written, err := io.Copy(buffer, limitReader)
|
||
if err != nil {
|
||
return "", "", fmt.Errorf("failed to read image data: %w", err)
|
||
}
|
||
if written >= maxImageSize {
|
||
return "", "", fmt.Errorf("image size exceeds maximum allowed size of %d bytes", maxImageSize)
|
||
}
|
||
|
||
data = base64.StdEncoding.EncodeToString(buffer.Bytes())
|
||
mimeType = contentType
|
||
|
||
// Handle application/octet-stream type
|
||
if mimeType == "application/octet-stream" {
|
||
_, format, _, err := DecodeBase64ImageData(data)
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
mimeType = "image/" + format
|
||
}
|
||
|
||
return mimeType, data, nil
|
||
}
|
||
|
||
func DecodeUrlImageData(imageUrl string) (image.Config, string, error) {
|
||
response, err := DoDownloadRequest(imageUrl)
|
||
if err != nil {
|
||
common.SysLog(fmt.Sprintf("fail to get image from url: %s", err.Error()))
|
||
return image.Config{}, "", err
|
||
}
|
||
defer response.Body.Close()
|
||
|
||
if response.StatusCode != 200 {
|
||
err = errors.New(fmt.Sprintf("fail to get image from url: %s", response.Status))
|
||
return image.Config{}, "", err
|
||
}
|
||
|
||
mimeType := response.Header.Get("Content-Type")
|
||
|
||
if mimeType != "application/octet-stream" && !strings.HasPrefix(mimeType, "image/") {
|
||
return image.Config{}, "", fmt.Errorf("invalid content type: %s, required image/*", mimeType)
|
||
}
|
||
|
||
var readData []byte
|
||
for _, limit := range []int64{1024 * 8, 1024 * 24, 1024 * 64} {
|
||
common.SysLog(fmt.Sprintf("try to decode image config with limit: %d", limit))
|
||
|
||
// 从response.Body读取更多的数据直到达到当前的限制
|
||
additionalData := make([]byte, limit-int64(len(readData)))
|
||
n, _ := io.ReadFull(response.Body, additionalData)
|
||
readData = append(readData, additionalData[:n]...)
|
||
|
||
// 使用io.MultiReader组合已经读取的数据和response.Body
|
||
limitReader := io.MultiReader(bytes.NewReader(readData), response.Body)
|
||
|
||
var config image.Config
|
||
var format string
|
||
config, format, err = getImageConfig(limitReader)
|
||
if err == nil {
|
||
return config, format, nil
|
||
}
|
||
}
|
||
|
||
return image.Config{}, "", err // 返回最后一个错误
|
||
}
|
||
|
||
func getImageConfig(reader io.Reader) (image.Config, string, error) {
|
||
// 读取图片的头部信息来获取图片尺寸
|
||
config, format, err := image.DecodeConfig(reader)
|
||
if err != nil {
|
||
err = errors.New(fmt.Sprintf("fail to decode image config(gif, jpg, png): %s", err.Error()))
|
||
common.SysLog(err.Error())
|
||
config, err = webp.DecodeConfig(reader)
|
||
if err != nil {
|
||
err = errors.New(fmt.Sprintf("fail to decode image config(webp): %s", err.Error()))
|
||
common.SysLog(err.Error())
|
||
}
|
||
format = "webp"
|
||
}
|
||
if err != nil {
|
||
return image.Config{}, "", err
|
||
}
|
||
return config, format, nil
|
||
}
|