mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
opt: optimize image compress alg, add cache control for image
This commit is contained in:
parent
7c4dfe96ee
commit
57243ff010
@ -336,9 +336,11 @@ func staticResourceMiddleware() gin.HandlerFunc {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置图片缓存有效期为一年 (365天)
|
||||||
|
c.Header("Cache-Control", "max-age=31536000, public")
|
||||||
// 直接输出图像数据流
|
// 直接输出图像数据流
|
||||||
c.Data(http.StatusOK, "image/jpeg", buffer.Bytes())
|
c.Data(http.StatusOK, "image/jpeg", buffer.Bytes())
|
||||||
return
|
c.Abort() // 中断请求
|
||||||
}
|
}
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,11 @@ func main() {
|
|||||||
|
|
||||||
// MidJourney service pool
|
// MidJourney service pool
|
||||||
fx.Provide(mj.NewServicePool),
|
fx.Provide(mj.NewServicePool),
|
||||||
|
fx.Invoke(func(pool *mj.ServicePool) {
|
||||||
|
if pool.HasAvailableService() {
|
||||||
|
pool.DownloadImages()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
// Stable Diffusion 机器人
|
// Stable Diffusion 机器人
|
||||||
fx.Provide(sd.NewServicePool),
|
fx.Provide(sd.NewServicePool),
|
||||||
|
@ -4,7 +4,9 @@ import (
|
|||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
"chatplus/service/oss"
|
"chatplus/service/oss"
|
||||||
"chatplus/store"
|
"chatplus/store"
|
||||||
|
"chatplus/store/model"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -12,8 +14,10 @@ import (
|
|||||||
|
|
||||||
// ServicePool Mj service pool
|
// ServicePool Mj service pool
|
||||||
type ServicePool struct {
|
type ServicePool struct {
|
||||||
services []*Service
|
services []*Service
|
||||||
taskQueue *store.RedisQueue
|
taskQueue *store.RedisQueue
|
||||||
|
db *gorm.DB
|
||||||
|
uploaderManager *oss.UploaderManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig) *ServicePool {
|
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig) *ServicePool {
|
||||||
@ -29,7 +33,7 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
|||||||
|
|
||||||
name := fmt.Sprintf("MjService-%d", k)
|
name := fmt.Sprintf("MjService-%d", k)
|
||||||
// create mj service
|
// create mj service
|
||||||
service := NewService(name, queue, 4, 600, db, client, manager, appConfig.ProxyURL)
|
service := NewService(name, queue, 4, 600, db, client)
|
||||||
botName := fmt.Sprintf("MjBot-%d", k)
|
botName := fmt.Sprintf("MjBot-%d", k)
|
||||||
bot, err := NewBot(botName, appConfig.ProxyURL, &config, service)
|
bot, err := NewBot(botName, appConfig.ProxyURL, &config, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -50,11 +54,39 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &ServicePool{
|
return &ServicePool{
|
||||||
taskQueue: queue,
|
taskQueue: queue,
|
||||||
services: services,
|
services: services,
|
||||||
|
uploaderManager: manager,
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ServicePool) DownloadImages() {
|
||||||
|
go func() {
|
||||||
|
var items []model.MidJourneyJob
|
||||||
|
for {
|
||||||
|
res := p.db.Where("img_url = ? AND progress = ?", "", 100).Find(&items)
|
||||||
|
if res.Error != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// download images
|
||||||
|
for _, item := range items {
|
||||||
|
imgURL, err := p.uploaderManager.GetUploadHandler().PutImg(item.OrgURL, true)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error with download image: ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
item.ImgURL = imgURL
|
||||||
|
p.db.Updates(&item)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// PushTask push a new mj task in to task queue
|
// PushTask push a new mj task in to task queue
|
||||||
func (p *ServicePool) PushTask(task types.MjTask) {
|
func (p *ServicePool) PushTask(task types.MjTask) {
|
||||||
logger.Debugf("add a new MidJourney task to the task list: %+v", task)
|
logger.Debugf("add a new MidJourney task to the task list: %+v", task)
|
||||||
|
@ -2,7 +2,6 @@ package mj
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
"chatplus/service/oss"
|
|
||||||
"chatplus/store"
|
"chatplus/store"
|
||||||
"chatplus/store/model"
|
"chatplus/store/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -17,24 +16,20 @@ type Service struct {
|
|||||||
client *Client // MJ client
|
client *Client // MJ client
|
||||||
taskQueue *store.RedisQueue
|
taskQueue *store.RedisQueue
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
uploadManager *oss.UploaderManager
|
|
||||||
proxyURL string
|
|
||||||
maxHandleTaskNum int32 // max task number current service can handle
|
maxHandleTaskNum int32 // max task number current service can handle
|
||||||
handledTaskNum int32 // already handled task number
|
handledTaskNum int32 // already handled task number
|
||||||
taskStartTimes map[int]time.Time // task start time, to check if the task is timeout
|
taskStartTimes map[int]time.Time // task start time, to check if the task is timeout
|
||||||
taskTimeout int64
|
taskTimeout int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(name string, queue *store.RedisQueue, maxTaskNum int32, timeout int64, db *gorm.DB, client *Client, manager *oss.UploaderManager, proxy string) *Service {
|
func NewService(name string, queue *store.RedisQueue, maxTaskNum int32, timeout int64, db *gorm.DB, client *Client) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
name: name,
|
name: name,
|
||||||
db: db,
|
db: db,
|
||||||
taskQueue: queue,
|
taskQueue: queue,
|
||||||
client: client,
|
client: client,
|
||||||
uploadManager: manager,
|
|
||||||
taskTimeout: timeout,
|
taskTimeout: timeout,
|
||||||
maxHandleTaskNum: maxTaskNum,
|
maxHandleTaskNum: maxTaskNum,
|
||||||
proxyURL: proxy,
|
|
||||||
taskStartTimes: make(map[int]time.Time, 0),
|
taskStartTimes: make(map[int]time.Time, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,17 +141,6 @@ func (s *Service) Notify(data CBReq) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload image
|
|
||||||
if data.Status == Finished {
|
|
||||||
imgURL, err := s.uploadManager.GetUploadHandler().PutImg(data.Image.URL, true)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error with download img: ", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
job.ImgURL = imgURL
|
|
||||||
s.db.Updates(&job)
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.Status == Finished {
|
if data.Status == Finished {
|
||||||
// release lock task
|
// release lock task
|
||||||
atomic.AddInt32(&s.handledTaskNum, -1)
|
atomic.AddInt32(&s.handledTaskNum, -1)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4125778 */
|
font-family: "iconfont"; /* Project id 4125778 */
|
||||||
src: url('iconfont.woff2?t=1702024026523') format('woff2'),
|
src: url('iconfont.woff2?t=1703124384910') format('woff2'),
|
||||||
url('iconfont.woff?t=1702024026523') format('woff'),
|
url('iconfont.woff?t=1703124384910') format('woff'),
|
||||||
url('iconfont.ttf?t=1702024026523') format('truetype');
|
url('iconfont.ttf?t=1703124384910') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@ -13,6 +13,10 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-loading:before {
|
||||||
|
content: "\e627";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-alipay:before {
|
.icon-alipay:before {
|
||||||
content: "\e634";
|
content: "\e634";
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -5,6 +5,13 @@
|
|||||||
"css_prefix_text": "icon-",
|
"css_prefix_text": "icon-",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "1278349",
|
||||||
|
"name": "loading",
|
||||||
|
"font_class": "loading",
|
||||||
|
"unicode": "e627",
|
||||||
|
"unicode_decimal": 58919
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "1486848",
|
"icon_id": "1486848",
|
||||||
"name": "支付宝支付",
|
"name": "支付宝支付",
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -359,7 +359,7 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="job-item">
|
<div class="job-item">
|
||||||
<el-image
|
<el-image
|
||||||
:src="scope.item.type === 'upscale' ? scope.item['img_url'] + '?imageView2/1/w/240/h/300/q/75' : scope.item['img_url'] + '?imageView2/1/w/240/h/240/q/75'"
|
:src="scope.item.type === 'upscale' ? scope.item['img_url'] + '?imageView2/1/w/480/h/600/q/75' : scope.item['img_url'] + '?imageView2/1/w/480/h/480/q/75'"
|
||||||
:class="scope.item.type === 'upscale' ? 'upscale' : ''" :zoom-rate="1.2"
|
:class="scope.item.type === 'upscale' ? 'upscale' : ''" :zoom-rate="1.2"
|
||||||
:preview-src-list="[scope.item['img_url']]" fit="cover" :initial-index="scope.index"
|
:preview-src-list="[scope.item['img_url']]" fit="cover" :initial-index="scope.index"
|
||||||
loading="lazy" v-if="scope.item.progress > 0">
|
loading="lazy" v-if="scope.item.progress > 0">
|
||||||
@ -370,7 +370,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="image-slot">
|
<div class="image-slot" v-if="scope.item['img_url'] === ''">
|
||||||
|
<i class="iconfont icon-loading"></i>
|
||||||
|
<span>正在下载图片</span>
|
||||||
|
</div>
|
||||||
|
<div class="image-slot" v-else>
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<Picture/>
|
<Picture/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -596,7 +600,23 @@ const fetchRunningJobs = (userId) => {
|
|||||||
const fetchFinishJobs = (userId) => {
|
const fetchFinishJobs = (userId) => {
|
||||||
// 获取已完成的任务
|
// 获取已完成的任务
|
||||||
httpGet(`/api/mj/jobs?status=1&user_id=${userId}`).then(res => {
|
httpGet(`/api/mj/jobs?status=1&user_id=${userId}`).then(res => {
|
||||||
finishedJobs.value = res.data
|
if (finishedJobs.value.length === 0) {
|
||||||
|
finishedJobs.value = res.data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the img url is changed
|
||||||
|
const list = res.data
|
||||||
|
let changed = false
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (list[i]["img_url"] !== finishedJobs.value[i]["img_url"]) {
|
||||||
|
changed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
finishedJobs.value = list
|
||||||
|
}
|
||||||
setTimeout(() => fetchFinishJobs(userId), 1000)
|
setTimeout(() => fetchFinishJobs(userId), 1000)
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("获取任务失败:" + e.message)
|
ElMessage.error("获取任务失败:" + e.message)
|
||||||
|
@ -612,7 +612,23 @@ onMounted(() => {
|
|||||||
// 获取已完成的任务
|
// 获取已完成的任务
|
||||||
const fetchFinishJobs = (userId) => {
|
const fetchFinishJobs = (userId) => {
|
||||||
httpGet(`/api/sd/jobs?status=1&user_id=${userId}`).then(res => {
|
httpGet(`/api/sd/jobs?status=1&user_id=${userId}`).then(res => {
|
||||||
finishedJobs.value = res.data
|
if (finishedJobs.value.length === 0) {
|
||||||
|
finishedJobs.value = res.data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the img url is changed
|
||||||
|
const list = res.data
|
||||||
|
let changed = false
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (list[i]["img_url"] !== finishedJobs.value[i]["img_url"]) {
|
||||||
|
changed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
finishedJobs.value = list
|
||||||
|
}
|
||||||
setTimeout(() => fetchFinishJobs(userId), 1000)
|
setTimeout(() => fetchFinishJobs(userId), 1000)
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("获取任务失败:" + e.message)
|
ElMessage.error("获取任务失败:" + e.message)
|
||||||
|
@ -11,7 +11,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="waterfall" :style="{ height:listBoxHeight + 'px' }" id="waterfall-box">
|
<div class="waterfall" :style="{ height:listBoxHeight + 'px' }" id="waterfall-box">
|
||||||
<v3-waterfall id="waterfall" :list="list" srcKey="img_thumb"
|
<v3-waterfall v-if="imgType === 'mj'"
|
||||||
|
id="waterfall"
|
||||||
|
:list="list"
|
||||||
|
srcKey="img_thumb"
|
||||||
:gap="12"
|
:gap="12"
|
||||||
:bottomGap="-5"
|
:bottomGap="-5"
|
||||||
:colWidth="colWidth"
|
:colWidth="colWidth"
|
||||||
@ -21,7 +24,7 @@
|
|||||||
@scrollReachBottom="getNext">
|
@scrollReachBottom="getNext">
|
||||||
<template #default="slotProp">
|
<template #default="slotProp">
|
||||||
<div class="list-item">
|
<div class="list-item">
|
||||||
<div class="image" v-if="imgType === 'mj'">
|
<div class="image">
|
||||||
<el-image :src="slotProp.item['img_thumb']"
|
<el-image :src="slotProp.item['img_thumb']"
|
||||||
:zoom-rate="1.2"
|
:zoom-rate="1.2"
|
||||||
:preview-src-list="[slotProp.item['img_url']]"
|
:preview-src-list="[slotProp.item['img_url']]"
|
||||||
@ -43,7 +46,30 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-image>
|
</el-image>
|
||||||
</div>
|
</div>
|
||||||
<div class="image" v-else>
|
<div class="prompt">
|
||||||
|
<span>{{ slotProp.item.prompt }}</span>
|
||||||
|
<el-icon class="copy-prompt" :data-clipboard-text="slotProp.item.prompt">
|
||||||
|
<DocumentCopy/>
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v3-waterfall>
|
||||||
|
|
||||||
|
<v3-waterfall v-else
|
||||||
|
id="waterfall"
|
||||||
|
:list="list"
|
||||||
|
srcKey="img_thumb"
|
||||||
|
:gap="12"
|
||||||
|
:bottomGap="-5"
|
||||||
|
:colWidth="colWidth"
|
||||||
|
:distanceToScroll="100"
|
||||||
|
:isLoading="loading"
|
||||||
|
:isOver="false"
|
||||||
|
@scrollReachBottom="getNext">
|
||||||
|
<template #default="slotProp">
|
||||||
|
<div class="list-item">
|
||||||
|
<div class="image">
|
||||||
<el-image :src="slotProp.item['img_thumb']" loading="lazy"
|
<el-image :src="slotProp.item['img_thumb']" loading="lazy"
|
||||||
@click="showTask(slotProp.item)">
|
@click="showTask(slotProp.item)">
|
||||||
<template #placeholder>
|
<template #placeholder>
|
||||||
|
Loading…
Reference in New Issue
Block a user