luma page, upload image and remove image function is ready

This commit is contained in:
RockYang 2024-08-26 17:59:05 +08:00
parent f71a2f5263
commit ab421f2185
7 changed files with 200 additions and 81 deletions

View File

@ -26,7 +26,6 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"runtime/debug"
"strings"
"time"
@ -228,6 +227,7 @@ func needLogin(c *gin.Context) bool {
c.Request.URL.Path == "/api/suno/client" ||
c.Request.URL.Path == "/api/suno/detail" ||
c.Request.URL.Path == "/api/suno/play" ||
c.Request.URL.Path == "/api/download" ||
strings.HasPrefix(c.Request.URL.Path, "/api/test") ||
strings.HasPrefix(c.Request.URL.Path, "/api/user/clogin") ||
strings.HasPrefix(c.Request.URL.Path, "/api/config/") ||
@ -316,64 +316,58 @@ func staticResourceMiddleware() gin.HandlerFunc {
url := c.Request.URL.String()
// 拦截生成缩略图请求
if strings.HasPrefix(url, "/static/") {
if strings.Contains(url, "?imageView2") {
r := strings.SplitAfter(url, "imageView2")
size := strings.Split(r[1], "/")
if len(size) != 8 {
c.String(http.StatusNotFound, "invalid thumb args")
return
}
with := utils.IntValue(size[3], 0)
height := utils.IntValue(size[5], 0)
quality := utils.IntValue(size[7], 75)
// 打开图片文件
filePath := strings.TrimLeft(c.Request.URL.Path, "/")
file, err := os.Open(filePath)
if err != nil {
c.String(http.StatusNotFound, "Image not found")
return
}
defer file.Close()
// 解码图片
img, _, err := image.Decode(file)
// for .webp image
if err != nil {
img, err = webp.Decode(file)
}
if err != nil {
c.String(http.StatusInternalServerError, "Error decoding image")
return
}
var newImg image.Image
if height == 0 || with == 0 {
// 固定宽度,高度自适应
newImg = resize.Resize(uint(with), uint(height), img, resize.Lanczos3)
} else {
// 生成缩略图
newImg = resize.Thumbnail(uint(with), uint(height), img, resize.Lanczos3)
}
var buffer bytes.Buffer
err = jpeg.Encode(&buffer, newImg, &jpeg.Options{Quality: quality})
if err != nil {
logger.Error(err)
c.String(http.StatusInternalServerError, err.Error())
return
}
// 设置图片缓存有效期为一年 (365天)
c.Header("Cache-Control", "max-age=31536000, public")
// 直接输出图像数据流
c.Data(http.StatusOK, "image/jpeg", buffer.Bytes())
c.Abort() // 中断请求
} else if strings.Contains(url, "?download=true") {
filename := filepath.Base(url)
c.Header("Content-Disposition", "attachment; filename="+filename)
c.Header("Content-Type", "application/octet-stream")
if strings.HasPrefix(url, "/static/") && strings.Contains(url, "?imageView2") {
r := strings.SplitAfter(url, "imageView2")
size := strings.Split(r[1], "/")
if len(size) != 8 {
c.String(http.StatusNotFound, "invalid thumb args")
return
}
with := utils.IntValue(size[3], 0)
height := utils.IntValue(size[5], 0)
quality := utils.IntValue(size[7], 75)
// 打开图片文件
filePath := strings.TrimLeft(c.Request.URL.Path, "/")
file, err := os.Open(filePath)
if err != nil {
c.String(http.StatusNotFound, "Image not found")
return
}
defer file.Close()
// 解码图片
img, _, err := image.Decode(file)
// for .webp image
if err != nil {
img, err = webp.Decode(file)
}
if err != nil {
c.String(http.StatusInternalServerError, "Error decoding image")
return
}
var newImg image.Image
if height == 0 || with == 0 {
// 固定宽度,高度自适应
newImg = resize.Resize(uint(with), uint(height), img, resize.Lanczos3)
} else {
// 生成缩略图
newImg = resize.Thumbnail(uint(with), uint(height), img, resize.Lanczos3)
}
var buffer bytes.Buffer
err = jpeg.Encode(&buffer, newImg, &jpeg.Options{Quality: quality})
if err != nil {
logger.Error(err)
c.String(http.StatusInternalServerError, err.Error())
return
}
// 设置图片缓存有效期为一年 (365天)
c.Header("Cache-Control", "max-age=31536000, public")
// 直接输出图像数据流
c.Data(http.StatusOK, "image/jpeg", buffer.Bytes())
c.Abort() // 中断请求
}
c.Next()

View File

@ -17,19 +17,21 @@ import (
"geekai/utils/resp"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"io"
"net/http"
"time"
)
type UploadHandler struct {
type NetHandler struct {
BaseHandler
uploaderManager *oss.UploaderManager
}
func NewUploadHandler(app *core.AppServer, db *gorm.DB, manager *oss.UploaderManager) *UploadHandler {
return &UploadHandler{BaseHandler: BaseHandler{App: app, DB: db}, uploaderManager: manager}
func NewNetHandler(app *core.AppServer, db *gorm.DB, manager *oss.UploaderManager) *NetHandler {
return &NetHandler{BaseHandler: BaseHandler{App: app, DB: db}, uploaderManager: manager}
}
func (h *UploadHandler) Upload(c *gin.Context) {
func (h *NetHandler) Upload(c *gin.Context) {
file, err := h.uploaderManager.GetUploadHandler().PutFile(c, "file")
if err != nil {
resp.ERROR(c, err.Error())
@ -60,7 +62,7 @@ func (h *UploadHandler) Upload(c *gin.Context) {
resp.SUCCESS(c, file)
}
func (h *UploadHandler) List(c *gin.Context) {
func (h *NetHandler) List(c *gin.Context) {
var data struct {
Urls []string `json:"urls,omitempty"`
}
@ -95,7 +97,7 @@ func (h *UploadHandler) List(c *gin.Context) {
}
// Remove remove files
func (h *UploadHandler) Remove(c *gin.Context) {
func (h *NetHandler) Remove(c *gin.Context) {
userId := h.GetLoginUserId(c)
id := h.GetInt(c, "id", 0)
var file model.File
@ -119,3 +121,28 @@ func (h *UploadHandler) Remove(c *gin.Context) {
_ = h.uploaderManager.GetUploadHandler().Delete(objectKey)
resp.SUCCESS(c)
}
func (h *NetHandler) Download(c *gin.Context) {
fileUrl := c.Query("url")
// 使用http工具下载文件
if fileUrl == "" {
resp.ERROR(c, types.InvalidArgs)
return
}
// 使用http.Get下载文件
r, err := http.Get(fileUrl)
if err != nil {
resp.ERROR(c, err.Error())
return
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
resp.ERROR(c, "error status"+r.Status)
return
}
// 将下载的文件内容写入响应
c.Status(http.StatusOK)
_, _ = io.Copy(c.Writer, r.Body)
}

View File

@ -128,7 +128,7 @@ func main() {
fx.Provide(handler.NewChatRoleHandler),
fx.Provide(handler.NewUserHandler),
fx.Provide(chatimpl.NewChatHandler),
fx.Provide(handler.NewUploadHandler),
fx.Provide(handler.NewNetHandler),
fx.Provide(handler.NewSmsHandler),
fx.Provide(handler.NewRedeemHandler),
fx.Provide(handler.NewCaptchaHandler),
@ -249,10 +249,11 @@ func main() {
group.POST("tokens", h.Tokens)
group.GET("stop", h.StopGenerate)
}),
fx.Invoke(func(s *core.AppServer, h *handler.UploadHandler) {
fx.Invoke(func(s *core.AppServer, h *handler.NetHandler) {
s.Engine.POST("/api/upload", h.Upload)
s.Engine.POST("/api/upload/list", h.List)
s.Engine.GET("/api/upload/remove", h.Remove)
s.Engine.GET("/api/download", h.Download)
}),
fx.Invoke(func(s *core.AppServer, h *handler.SmsHandler) {
group := s.Engine.Group("/api/sms/")

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -91,6 +91,22 @@
position relative
}
}
.params {
display flex
justify-content right
color #e1e1e1
font-size 14px
padding 10px 30px
.item-group {
margin-left 20px
.label {
margin-right 5px
position relative
top 1px
}
}
}
}
}
@ -150,7 +166,16 @@
font-size 14px
.iconfont {
font-size 12px
font-size 11px
position relative
margin-right 5px
top -2px
}
.el-image {
width 14px
height 14px
margin-right 5px
}
&:hover {

View File

@ -69,3 +69,17 @@ export function httpPost(url, data = {}, options = {}) {
})
})
}
export function httpDownload(url) {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: url,
responseType: 'blob' // 将响应类型设置为 `blob`
}).then(response => {
resolve(response)
}).catch(err => {
reject(err)
})
})
}

View File

@ -2,15 +2,24 @@
<div class="page-luma">
<div class="prompt-box">
<div class="images">
<div v-for="img in images" class="item">
<div v-for="img in images" :key="img" class="item">
<el-image :src="img" fit="cover"/>
<el-icon><CircleCloseFilled /></el-icon>
<el-icon @click="remove(img)"><CircleCloseFilled /></el-icon>
</div>
</div>
<div class="prompt-container">
<div class="input-container">
<div class="upload-icon">
<i class="iconfont icon-image"></i>
<el-upload
class="avatar-uploader"
:auto-upload="true"
:show-file-list="false"
:http-request="upload"
accept=".jpg,.png,.jpeg"
>
<i class="iconfont icon-image"></i>
</el-upload>
</div>
<textarea
class="prompt-input"
@ -24,6 +33,16 @@
</div>
</div>
<div class="params">
<div class="item-group">
<span class="label">循环</span>
<el-switch v-model="loop" size="small" style="--el-switch-on-color:#BF78BF;" />
</div>
<div class="item-group">
<span class="label">提示词优化</span>
<el-switch v-model="promptExtend" size="small" style="--el-switch-on-color:#BF78BF;" />
</div>
</div>
</div>
</div>
@ -40,12 +59,11 @@
</div>
<div class="video-name">{{item.name}}</div>
<div class="opts">
<a :href="item.url+'?download=true'" download="video.mp4">
<button class="btn">
<i class="iconfont icon-download"></i>
<span>下载</span>
</button>
</a>
<button class="btn" @click="download(item)" :disabled="item.downloading">
<i class="iconfont icon-download" v-if="!item.downloading"></i>
<el-image src="/images/loading.gif" fit="cover" v-else />
<span>下载</span>
</button>
</div>
</el-col>
</el-row>
@ -56,19 +74,21 @@
<script setup>
import {ref} from "vue";
import {CircleCloseFilled} from "@element-plus/icons-vue";
import {httpDownload, httpPost} from "@/utils/http";
import {showMessageError} from "@/utils/dialog";
import {ElMessage} from "element-plus";
const row = ref(1)
const prompt = ref('')
const images = ref([
"http://nk.img.r9it.com/chatgpt-plus/1719371605709871.jpg",
"http://nk.img.r9it.com/chatgpt-plus/1719371605709871.jpg"
])
const loop = ref(false)
const promptExtend = ref(false)
const images = ref([])
const videos = ref([
{
id: 1,
name: 'a dancing girl',
url: 'http://localhost:5678/static/upload/2024/8/1724574661747320.mp4',
url: 'https://storage.cdn-luma.com/dream_machine/d133794f-3124-4059-a9f2-e5fed79f0d5b/watermarked_video01944f69966f14d33b6c4486a8cfb8dde.mp4',
cover: 'https://storage.cdn-luma.com/dream_machine/d133794f-3124-4059-a9f2-e5fed79f0d5b/video_0_thumb.jpg',
playing: false
},
@ -102,6 +122,44 @@ const videos = ref([
},
])
const download = (item) => {
const downloadURL = `${process.env.VUE_APP_API_HOST}/api/download?url=${item.url}`
// parse filename
const urlObj = new URL(item.url);
const fileName = urlObj.pathname.split('/').pop();
item.downloading = true
httpDownload(downloadURL).then(response => {
const blob = new Blob([response.data]);
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
item.downloading = false
}).catch(() => {
showMessageError("下载失败")
item.downloading = false
})
}
const upload = (file) => {
const formData = new FormData();
formData.append('file', file.file, file.name);
//
httpPost('/api/upload', formData).then((res) => {
images.value.push(res.data.url)
ElMessage.success({message: "上传成功", duration: 500})
}).catch((e) => {
ElMessage.error('图片上传失败:' + e.message)
})
};
const remove = (img) => {
images.value = images.value.filter(item => item !== img)
}
</script>