From e2ebdc06210a45172d65177b7abde66295d3fceb Mon Sep 17 00:00:00 2001 From: junleea <354425203@qq.com> Date: Sat, 6 Sep 2025 13:54:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=8B=E8=BD=BD=E4=BB=A3?= =?UTF-8?q?=E7=90=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler/tool.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ proto/conf.go | 1 + 2 files changed, 73 insertions(+) diff --git a/handler/tool.go b/handler/tool.go index a5192ef..9342803 100644 --- a/handler/tool.go +++ b/handler/tool.go @@ -6,7 +6,9 @@ import ( "io" "log" "net/http" + "net/url" "os" + "path/filepath" "strconv" "time" "videoplayer/dao" @@ -57,6 +59,8 @@ func SetUpToolGroup(router *gin.Engine) { toolGroup.POST("/del_monitor", DelMonitor) //删除设备监控 //发送邮件 toolGroup.POST("/send_mail", SendMailTool) + //下载代理 + toolGroup.GET("/dl/:url", DownloadProxyHandle) } func GetMonitorList(c *gin.Context) { @@ -485,3 +489,71 @@ func SendMailTool(c *gin.Context) { } } + +// DownloadProxyHandle 处理下载代理请求 +func DownloadProxyHandle(c *gin.Context) { + key := c.Query("key") + if key == "" || key != proto.Config.DOWNLOAD_PROXY_KEY { + c.JSON(http.StatusOK, gin.H{"code": proto.ParameterError, "message": "failed, key is null or error"}) + } + // 获取URL参数 + encodedURL := c.Param("url") + if encodedURL == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "URL参数不能为空", "code": proto.ParameterError, "message": "failed"}) + return + } + + // URL解码 + decodedURL, err := url.QueryUnescape(encodedURL) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "URL解码失败: " + err.Error(), "code": proto.ParameterError, "message": "failed"}) + return + } + + // 验证URL格式 + parsedURL, err := url.Parse(decodedURL) + if err != nil || parsedURL.Scheme == "" || parsedURL.Host == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "无效的URL格式", "code": proto.ParameterError, "message": "failed"}) + return + } + + // 发起请求获取目标资源 + resp, err := http.Get(decodedURL) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "获取资源失败: " + err.Error(), "code": proto.InternalServerError}) + return + } + defer resp.Body.Close() + + // 检查响应状态 + if resp.StatusCode != http.StatusOK { + c.JSON(http.StatusBadGateway, gin.H{"error": "目标资源请求失败,状态码: " + resp.Status, "code": proto.InternalServerError}) + return + } + + // 获取文件名 + filename := getFilenameFromURL(parsedURL) + + // 设置响应头,告诉浏览器这是一个文件下载 + c.Header("Content-Disposition", "attachment; filename="+url.QueryEscape(filename)) + c.Header("Content-Type", resp.Header.Get("Content-Type")) + c.Header("Content-Length", resp.Header.Get("Content-Length")) + + // 将响应体流式传输到客户端 + _, err = io.Copy(c.Writer, resp.Body) + if err != nil { + // 已经开始传输数据后发生错误,无法返回JSON,只能记录日志 + log.Println("tool download proxy handle error:", err) + } +} + +// 从URL中提取文件名 +func getFilenameFromURL(u *url.URL) string { + // 从路径中提取文件名 + filename := filepath.Base(u.Path) + // 如果路径中没有文件名,尝试从查询参数中获取 + if filename == "" || filename == "/" { + filename = "download_file" + } + return filename +} diff --git a/proto/conf.go b/proto/conf.go index 71d5dbe..0db119b 100644 --- a/proto/conf.go +++ b/proto/conf.go @@ -100,6 +100,7 @@ type ConfigStruct struct { MONITOR_SERVER_TOKEN string `json:"monitor_server_token"` // 监控服务器token,用于状态监控及邮件通知 APP_ID string `json:"app_id"` // 应用ID,用于标识不同应用 MONITOR_MAIL []StructValue `json:"monitor_mail"` // 设备监控邮件通知配置 + DOWNLOAD_PROXY_KEY string `json:"download_proxy_key"` // 下载代理key } func WriteConfigToFile() {