mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
feat: add oss service factory implements, add support for setting custom upload handler, localstorage and minio oss
This commit is contained in:
parent
5a0f272fa8
commit
3dbeb1ccb6
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
57
api/service/oss/localstorage_service.go
Normal file
57
api/service/oss/localstorage_service.go
Normal 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{}
|
78
api/service/oss/minio_service.go
Normal file
78
api/service/oss/minio_service.go
Normal 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{}
|
8
api/service/oss/uploader.go
Normal file
8
api/service/oss/uploader.go
Normal 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)
|
||||
}
|
37
api/service/oss/uploader_manager.go
Normal file
37
api/service/oss/uploader_manager.go
Normal 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]
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
Loading…
Reference in New Issue
Block a user