diff --git a/api/core/app_server.go b/api/core/app_server.go index fab16d1c..92967067 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -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() diff --git a/api/handler/upload_handler.go b/api/handler/upload_handler.go index 9d2c8706..fd7f437d 100644 --- a/api/handler/upload_handler.go +++ b/api/handler/upload_handler.go @@ -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) +} diff --git a/api/main.go b/api/main.go index 58a428be..2cf9a407 100644 --- a/api/main.go +++ b/api/main.go @@ -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/") diff --git a/web/public/images/loading.gif b/web/public/images/loading.gif new file mode 100644 index 00000000..77eb7b88 Binary files /dev/null and b/web/public/images/loading.gif differ diff --git a/web/src/assets/css/luma.styl b/web/src/assets/css/luma.styl index 557ff651..7d773b3f 100644 --- a/web/src/assets/css/luma.styl +++ b/web/src/assets/css/luma.styl @@ -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 { diff --git a/web/src/utils/http.js b/web/src/utils/http.js index 9399b141..3329591f 100644 --- a/web/src/utils/http.js +++ b/web/src/utils/http.js @@ -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) + }) + }) +} \ No newline at end of file diff --git a/web/src/views/Luma.vue b/web/src/views/Luma.vue index 94c6cbd7..8f3d8e8e 100644 --- a/web/src/views/Luma.vue +++ b/web/src/views/Luma.vue @@ -2,15 +2,24 @@