feat: add oss service factory implements, add support for setting custom upload handler, localstorage and minio oss

This commit is contained in:
RockYang 2023-08-20 22:29:08 +08:00
parent 5a0f272fa8
commit 3dbeb1ccb6
12 changed files with 227 additions and 100 deletions

View File

@ -19,7 +19,8 @@ type AppConfig struct {
AesEncryptKey string
SmsConfig AliYunSmsConfig // AliYun send message service config
ExtConfig ChatPlusExtConfig // ChatPlus extensions callback api config
MinioConfig MinioConfig
OSS OSSConfig // OSS config
}
type ChatPlusApiConfig struct {
@ -40,6 +41,11 @@ type AliYunSmsConfig struct {
Domain string
}
type OSSConfig struct {
Active string
Local LocalStorageConfig
Minio MinioConfig
}
type MinioConfig struct {
Endpoint string
AccessKey string
@ -49,6 +55,11 @@ type MinioConfig struct {
Domain string
}
type LocalStorageConfig struct {
BasePath string
BaseURL string
}
type RedisConfig struct {
Host string
Port int

View File

@ -4,6 +4,7 @@ import (
"chatplus/core"
"chatplus/core/types"
"chatplus/service/function"
"chatplus/service/oss"
"chatplus/store"
"chatplus/store/model"
"chatplus/utils"
@ -36,23 +37,23 @@ type Image struct {
type MidJourneyHandler struct {
BaseHandler
leveldb *store.LevelDB
db *gorm.DB
mjFunc function.FuncMidJourney
//minio *service.MinioService
leveldb *store.LevelDB
db *gorm.DB
mjFunc function.FuncMidJourney
uploaderManager *oss.UploaderManager
}
func NewMidJourneyHandler(
app *core.AppServer,
leveldb *store.LevelDB,
db *gorm.DB,
//minio *service.MinioService,
manager *oss.UploaderManager,
functions map[string]function.Function) *MidJourneyHandler {
h := MidJourneyHandler{
leveldb: leveldb,
db: db,
//minio: minio,
mjFunc: functions[types.FuncMidJourney].(function.FuncMidJourney)}
leveldb: leveldb,
db: db,
uploaderManager: manager,
mjFunc: functions[types.FuncMidJourney].(function.FuncMidJourney)}
h.App = app
return &h
}
@ -98,22 +99,15 @@ func (h *MidJourneyHandler) Notify(c *gin.Context) {
resp.SUCCESS(c)
return
}
// TODO: 下载本地或者 OSS提供可配置的选项
// 下载图片到本地服务器
filePath, err := utils.GenUploadPath(h.App.Config.StaticDir, data.Image.Filename)
if err != nil {
logger.Error("error with generate image dir: ", err)
resp.SUCCESS(c)
return
}
err = utils.DownloadFile(data.Image.URL, filePath, h.App.Config.ProxyURL)
// download image
imgURL, err := h.uploaderManager.GetActiveService().PutImg(data.Image.URL)
if err != nil {
logger.Error("error with download image: ", err)
resp.SUCCESS(c)
return
}
data.Image.URL = utils.GenUploadUrl(h.App.Config.StaticDir, h.App.Config.StaticUrl, filePath)
data.Image.URL = imgURL
message := model.HistoryMessage{
UserId: task.UserId,
ChatId: task.ChatId,

View File

@ -2,7 +2,7 @@ package handler
import (
"chatplus/core"
"chatplus/utils"
"chatplus/service/oss"
"chatplus/utils/resp"
"fmt"
"github.com/gin-gonic/gin"
@ -11,33 +11,22 @@ import (
type UploadHandler struct {
BaseHandler
db *gorm.DB
db *gorm.DB
uploaderManager *oss.UploaderManager
}
func NewUploadHandler(app *core.AppServer, db *gorm.DB) *UploadHandler {
handler := &UploadHandler{db: db}
func NewUploadHandler(app *core.AppServer, db *gorm.DB, manager *oss.UploaderManager) *UploadHandler {
handler := &UploadHandler{db: db, uploaderManager: manager}
handler.App = app
return handler
}
func (h *UploadHandler) Upload(c *gin.Context) {
file, err := c.FormFile("file")
fileURL, err := h.uploaderManager.GetActiveService().PutFile(c)
if err != nil {
resp.ERROR(c, fmt.Sprintf("文件上传失败: %s", err.Error()))
return
}
filePath, err := utils.GenUploadPath(h.App.Config.StaticDir, file.Filename)
if err != nil {
resp.ERROR(c, fmt.Sprintf("文件上传失败: %s", err.Error()))
return
}
// 将文件保存到指定路径
err = c.SaveUploadedFile(file, filePath)
if err != nil {
resp.ERROR(c, fmt.Sprintf("文件保存失败: %s", err.Error()))
return
}
resp.SUCCESS(c, utils.GenUploadUrl(h.App.Config.StaticDir, h.App.Config.StaticUrl, filePath))
resp.SUCCESS(c, fileURL)
}

View File

@ -8,6 +8,7 @@ import (
logger2 "chatplus/logger"
"chatplus/service"
"chatplus/service/function"
"chatplus/service/oss"
"chatplus/store"
"context"
"embed"
@ -129,6 +130,7 @@ func main() {
fx.Provide(func(config *types.AppConfig) *service.CaptchaService {
return service.NewCaptchaService(config.ApiConfig)
}),
fx.Provide(oss.NewUploaderManager),
// 注册路由
fx.Invoke(func(s *core.AppServer, h *handler.ChatRoleHandler) {

View File

@ -1,54 +0,0 @@
package service
import (
"chatplus/core/types"
"chatplus/utils"
"context"
"fmt"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"net/url"
"path"
"strings"
)
type MinioService struct {
config *types.AppConfig
client *minio.Client
}
func NewMinioService(config *types.AppConfig) (*MinioService, error) {
minioClient, err := minio.New(config.MinioConfig.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(config.MinioConfig.AccessKey, config.MinioConfig.AccessSecret, ""),
Secure: config.MinioConfig.UseSSL,
})
if err != nil {
return nil, err
}
return &MinioService{config: config, client: minioClient}, nil
}
func (s *MinioService) UploadMjImg(imageURL string) (string, error) {
parsedURL, err := url.Parse(imageURL)
if err != nil {
return "", err
}
filename := path.Base(parsedURL.Path)
imageBytes, err := utils.DownloadImage(imageURL, s.config.ProxyURL)
if err != nil {
return "", err
}
info, err := s.client.PutObject(
context.Background(),
s.config.MinioConfig.Bucket,
filename,
strings.NewReader(string(imageBytes)),
int64(len(imageBytes)),
minio.PutObjectOptions{ContentType: "image/png"})
if err != nil {
return "", err
}
return fmt.Sprintf("%s/%s/%s", s.config.MinioConfig.Domain, s.config.MinioConfig.Bucket, info.Key), nil
}

View File

@ -0,0 +1,57 @@
package oss
import (
"chatplus/core/types"
"chatplus/utils"
"fmt"
"github.com/gin-gonic/gin"
"path/filepath"
)
type LocalStorageService struct {
config *types.LocalStorageConfig
proxyURL string
}
func NewLocalStorageService(config *types.AppConfig) LocalStorageService {
return LocalStorageService{
config: &config.OSS.Local,
proxyURL: config.ProxyURL,
}
}
func (s LocalStorageService) PutFile(ctx *gin.Context) (string, error) {
file, err := ctx.FormFile("file")
if err != nil {
return "", fmt.Errorf("error with get form: %v", err)
}
filePath, err := utils.GenUploadPath(s.config.BasePath, file.Filename)
if err != nil {
return "", fmt.Errorf("error with generate filename: %s", err.Error())
}
// 将文件保存到指定路径
err = ctx.SaveUploadedFile(file, filePath)
if err != nil {
return "", fmt.Errorf("error with save upload file: %s", err.Error())
}
return utils.GenUploadUrl(s.config.BasePath, s.config.BaseURL, filePath), nil
}
func (s LocalStorageService) PutImg(imageURL string) (string, error) {
filename := filepath.Base(imageURL)
filePath, err := utils.GenUploadPath(s.config.BasePath, filename)
if err != nil {
return "", fmt.Errorf("error with generate image dir: %v", err)
}
err = utils.DownloadFile(imageURL, filePath, s.proxyURL)
if err != nil {
return "", fmt.Errorf("error with download image: %v", err)
}
return utils.GenUploadUrl(s.config.BasePath, s.config.BaseURL, filePath), nil
}
var _ Uploader = LocalStorageService{}

View File

@ -0,0 +1,78 @@
package oss
import (
"chatplus/core/types"
"chatplus/utils"
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"path/filepath"
"strings"
"time"
)
type MinioService struct {
config *types.MinioConfig
client *minio.Client
proxyURL string
}
func NewMinioService(appConfig *types.AppConfig) (MinioService, error) {
config := &appConfig.OSS.Minio
minioClient, err := minio.New(config.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(config.AccessKey, config.AccessSecret, ""),
Secure: config.UseSSL,
})
if err != nil {
return MinioService{}, err
}
return MinioService{config: config, client: minioClient, proxyURL: appConfig.ProxyURL}, nil
}
func (s MinioService) PutImg(imageURL string) (string, error) {
imageData, err := utils.DownloadImage(imageURL, s.proxyURL)
if err != nil {
return "", fmt.Errorf("error with download image: %v", err)
}
fileExt := filepath.Ext(filepath.Base(imageURL))
filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), fileExt)
info, err := s.client.PutObject(
context.Background(),
s.config.Bucket,
filename,
strings.NewReader(string(imageData)),
int64(len(imageData)),
minio.PutObjectOptions{ContentType: "image/png"})
if err != nil {
return "", err
}
return fmt.Sprintf("%s/%s/%s", s.config.Domain, s.config.Bucket, info.Key), nil
}
func (s MinioService) PutFile(ctx *gin.Context) (string, error) {
file, err := ctx.FormFile("file")
if err != nil {
return "", fmt.Errorf("error with get form: %v", err)
}
// Open the uploaded file
fileReader, err := file.Open()
if err != nil {
return "", fmt.Errorf("error opening file: %v", err)
}
defer fileReader.Close()
fileExt := filepath.Ext(file.Filename)
filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), fileExt)
info, err := s.client.PutObject(ctx, s.config.Bucket, filename, fileReader, file.Size, minio.PutObjectOptions{
ContentType: file.Header.Get("Content-Type"),
})
if err != nil {
return "", fmt.Errorf("error uploading to MinIO: %v", err)
}
return fmt.Sprintf("%s/%s/%s", s.config.Domain, s.config.Bucket, info.Key), nil
}
var _ Uploader = MinioService{}

View File

@ -0,0 +1,8 @@
package oss
import "github.com/gin-gonic/gin"
type Uploader interface {
PutFile(ctx *gin.Context) (string, error)
PutImg(imageURL string) (string, error)
}

View File

@ -0,0 +1,37 @@
package oss
import (
"chatplus/core/types"
"strings"
)
type UploaderManager struct {
active string
uploadServices map[string]Uploader
}
const uploaderLocal = "LOCAL"
const uploaderMinio = "MINIO"
func NewUploaderManager(config *types.AppConfig) (*UploaderManager, error) {
services := make(map[string]Uploader)
if config.OSS.Minio.AccessKey != "" {
minioService, err := NewMinioService(config)
if err != nil {
return nil, err
}
services[uploaderMinio] = minioService
}
if config.OSS.Local.BasePath != "" {
services[uploaderLocal] = NewLocalStorageService(config)
}
active := uploaderLocal
if config.OSS.Active != "" {
active = strings.ToUpper(config.OSS.Active)
}
return &UploaderManager{uploadServices: services, active: active}, nil
}
func (m *UploaderManager) GetActiveService() Uploader {
return m.uploadServices[m.active]
}

View File

@ -4,7 +4,7 @@ import (
"bufio"
"chatplus/core"
"chatplus/core/types"
"chatplus/service"
"chatplus/service/oss"
"chatplus/utils"
"context"
"encoding/json"
@ -15,12 +15,15 @@ import (
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
func main() {
minio()
imageURL := "https://cdn.discordapp.com/attachments/1139552247693443184/1141619433752768572/lisamiller4099_A_beautiful_fairy_sister_from_Chinese_mythology__3162726e-5ee4-4f60-932b-6b78b375eaef.png"
fmt.Println(filepath.Ext(filepath.Base(imageURL)))
}
// Http client 取消操作
@ -174,7 +177,7 @@ func extractFunction() error {
func minio() {
config := core.NewDefaultConfig()
config.ProxyURL = "http://localhost:7777"
config.MinioConfig = types.MinioConfig{
config.OSS.Minio = types.MinioConfig{
Endpoint: "localhost:9010",
AccessKey: "ObWIEyXaQUHOYU26L0oI",
AccessSecret: "AJW3HHhlGrprfPcmiC7jSOSzVCyrlhX4AnOAUzqI",
@ -182,12 +185,12 @@ func minio() {
UseSSL: false,
Domain: "http://localhost:9010",
}
minioService, err := service.NewMinioService(config)
minioService, err := oss.NewMinioService(config)
if err != nil {
panic(err)
}
url, err := minioService.UploadMjImg("https://cdn.discordapp.com/attachments/1139552247693443184/1141619433752768572/lisamiller4099_A_beautiful_fairy_sister_from_Chinese_mythology__3162726e-5ee4-4f60-932b-6b78b375eaef.png")
url, err := minioService.PutImg("https://cdn.discordapp.com/attachments/1139552247693443184/1141619433752768572/lisamiller4099_A_beautiful_fairy_sister_from_Chinese_mythology__3162726e-5ee4-4f60-932b-6b78b375eaef.png")
if err != nil {
panic(err)
}

View File

@ -23,7 +23,7 @@ func GenUploadPath(basePath, filename string) (string, error) {
}
}
fileExt := filepath.Ext(filename)
return fmt.Sprintf("%s/%d%s", dir, now.UnixMilli(), fileExt), nil
return fmt.Sprintf("%s/%d%s", dir, now.UnixNano(), fileExt), nil
}
// GenUploadUrl 生成上传文件 URL

View File

@ -2,11 +2,13 @@ version: '3'
services:
minio:
image: minio/minio
container_name: minio
volumes:
- ./data:/data
ports:
- 9000:9000
- "9010:9000"
- "9011:9001"
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio@pass
command: server /data
command: server /data --console-address ":9001" --address ":9000"