package handler import ( "io" "mime" "net/http" "path/filepath" "github.com/gin-gonic/gin" "x-agents/server/internal/repository" "x-agents/server/internal/service" ) type UploadHandler struct { uploadService *service.UploadService knowledgeRepo *repository.KnowledgeRepository } func NewUploadHandler(uploadService *service.UploadService, knowledgeRepo *repository.KnowledgeRepository) *UploadHandler { return &UploadHandler{uploadService: uploadService, knowledgeRepo: knowledgeRepo} } // Upload 上传文件 func (h *UploadHandler) Upload(c *gin.Context) { file, err := c.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "No file uploaded"}) return } // 检查文件大小(最大 100MB) if file.Size > 100*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "File too large (max 100MB)"}) return } result, err := h.uploadService.Upload(file) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } if !result.Success { c.JSON(http.StatusInternalServerError, result) return } c.JSON(http.StatusOK, result) } // Delete 删除文件 func (h *UploadHandler) Delete(c *gin.Context) { filename := c.Param("filename") if filename == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Filename is required"}) return } if err := h.uploadService.DeleteFile(filename); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "File deleted"}) } // ProxyFile 代理文件访问(解决 MinIO 内网地址和 HTTPS 问题) func (h *UploadHandler) ProxyFile(c *gin.Context) { fileKey := c.Query("key") kbID := c.Query("kb_id") if fileKey == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "File key is required"}) return } var content io.Reader var contentType string // 如果提供了知识库 ID,使用知识库的存储配置 if kbID != "" && h.knowledgeRepo != nil { kb, err := h.knowledgeRepo.FindByID(kbID) if err == nil && kb.StorageConfig.Type == "minio" { reader, ct, err := h.uploadService.GetFileContentWithConfig(fileKey, kb.StorageConfig) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } content = reader contentType = ct defer reader.Close() } else { // 使用全局配置 reader, ct, err := h.uploadService.GetFileContent(fileKey) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } content = reader contentType = ct defer reader.Close() } } else { // 使用全局配置 reader, ct, err := h.uploadService.GetFileContent(fileKey) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": err.Error()}) return } content = reader contentType = ct defer reader.Close() } // 设置响应头 // 如果 Content-Type 是 application/octet-stream,根据文件扩展名推断 if contentType == "application/octet-stream" { ext := filepath.Ext(fileKey) if detected := mime.TypeByExtension(ext); detected != "" { contentType = detected } } if contentType != "" { c.Header("Content-Type", contentType) } else { c.Header("Content-Type", "application/octet-stream") } c.Header("Content-Disposition", "inline") // 直接流式输出文件内容 c.DataFromReader(http.StatusOK, -1, contentType, content, nil) }