PreviewSkillFile godoc @Summary 预览技能文件 @Description 流式预览技能包内指定文件内容 @Tags 技能库-后台 @Accept json @Produce octet-stream @Security BearerAuth @Param id path int true "技能ID" @Param path path string true "文件相对路径" @Success 200 {file} binary "文件内容" @Router /api/admin/skill-library/{id}/files-preview/{path} [g
(c *gin.Context)
| 672 | // @Success 200 {file} binary "文件内容" |
| 673 | // @Router /api/admin/skill-library/{id}/files-preview/{path} [get] |
| 674 | func PreviewSkillFile(c *gin.Context) { |
| 675 | skillID, err := strconv.ParseInt(c.Param("id"), 10, 64) |
| 676 | if err != nil || skillID <= 0 { |
| 677 | c.JSON(http.StatusBadRequest, model.ParamError.ToResponse(nil)) |
| 678 | return |
| 679 | } |
| 680 | |
| 681 | filePath := c.Param("path") |
| 682 | if filePath == "" { |
| 683 | c.JSON(http.StatusBadRequest, model.ParamError.ToErrorResponse(errors.New("file path is required"))) |
| 684 | return |
| 685 | } |
| 686 | filePath = strings.TrimPrefix(filePath, "/") |
| 687 | |
| 688 | eid := config.GetEID(c) |
| 689 | svc := service.NewSkillLibraryService() |
| 690 | |
| 691 | fileInfo, err := svc.GetSkillFileInfo(c.Request.Context(), eid, skillID, filePath) |
| 692 | if err != nil { |
| 693 | toSkillAdminErrorResponse(c, err) |
| 694 | return |
| 695 | } |
| 696 | |
| 697 | ext := strings.ToLower(filepath.Ext(filePath)) |
| 698 | contentType := "application/octet-stream" |
| 699 | switch ext { |
| 700 | case ".md": |
| 701 | contentType = "text/markdown; charset=utf-8" |
| 702 | case ".txt": |
| 703 | contentType = "text/plain; charset=utf-8" |
| 704 | case ".html", ".htm": |
| 705 | contentType = "text/html; charset=utf-8" |
| 706 | case ".json": |
| 707 | contentType = "application/json; charset=utf-8" |
| 708 | case ".yaml", ".yml": |
| 709 | contentType = "text/yaml; charset=utf-8" |
| 710 | case ".py": |
| 711 | contentType = "text/x-python; charset=utf-8" |
| 712 | case ".js": |
| 713 | contentType = "application/javascript; charset=utf-8" |
| 714 | case ".css": |
| 715 | contentType = "text/css; charset=utf-8" |
| 716 | } |
| 717 | |
| 718 | filename := filepath.Base(filePath) |
| 719 | encodedFilename := url.QueryEscape(filename) |
| 720 | c.Header("Content-Disposition", `inline; filename="`+filename+`"; filename*=UTF-8''`+encodedFilename) |
| 721 | c.Header("Content-Type", contentType) |
| 722 | c.Header("Content-Length", strconv.FormatInt(fileInfo.Size, 10)) |
| 723 | |
| 724 | file, err := os.Open(fileInfo.FullPath) |
| 725 | if err != nil { |
| 726 | c.JSON(http.StatusInternalServerError, model.FileError.ToResponse(err)) |
| 727 | return |
| 728 | } |
| 729 | defer file.Close() |
| 730 | |
| 731 | http.ServeContent(c.Writer, c.Request, filename, fileInfo.ModTime, file) |
nothing calls this directly
no test coverage detected