package example import ( "io" "strconv" "strings" "kra/internal/biz/example" "kra/pkg/response" "kra/pkg/utils" "github.com/gin-gonic/gin" ) // BreakpointContinue // @Tags ExaFileUploadAndDownload // @Summary 断点续传到服务器 // @Security ApiKeyAuth // @accept multipart/form-data // @Produce application/json // @Param file formData file true "断点续传示例" // @Success 200 {object} response.Response{msg=string} "断点续传到服务器" // @Router /fileUploadAndDownload/breakpointContinue [post] func (f *FileUploadApi) BreakpointContinue(c *gin.Context) { fileMd5 := c.Request.FormValue("fileMd5") fileName := c.Request.FormValue("fileName") chunkMd5 := c.Request.FormValue("chunkMd5") chunkNumber, _ := strconv.Atoi(c.Request.FormValue("chunkNumber")) chunkTotal, _ := strconv.Atoi(c.Request.FormValue("chunkTotal")) _, FileHeader, err := c.Request.FormFile("file") if err != nil { response.FailWithMessage("接收文件失败", c) return } file, err := FileHeader.Open() if err != nil { response.FailWithMessage("文件读取失败", c) return } defer file.Close() content, _ := io.ReadAll(file) if !utils.CheckMd5(content, chunkMd5) { response.FailWithMessage("检查md5失败", c) return } exaFile, err := breakpointContinueUsecase.FindOrCreateFile(c.Request.Context(), fileMd5, fileName, chunkTotal) if err != nil { response.FailWithMessage("查找或创建记录失败: "+err.Error(), c) return } pathC, err := utils.BreakPointContinue(content, fileName, chunkNumber, chunkTotal, fileMd5) if err != nil { response.FailWithMessage("断点续传失败: "+err.Error(), c) return } if err = breakpointContinueUsecase.CreateFileChunk(c.Request.Context(), exaFile.ID, pathC, chunkNumber); err != nil { response.FailWithMessage("创建文件记录失败: "+err.Error(), c) return } response.OkWithMessage("切片创建成功", c) } // FindFile // @Tags ExaFileUploadAndDownload // @Summary 查找文件 // @Security ApiKeyAuth // @accept multipart/form-data // @Produce application/json // @Param fileMd5 query string true "文件MD5" // @Param fileName query string true "文件名" // @Param chunkTotal query int true "切片总数" // @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "查找文件,返回包括文件详情" // @Router /fileUploadAndDownload/findFile [get] func (f *FileUploadApi) FindFile(c *gin.Context) { fileMd5 := c.Query("fileMd5") fileName := c.Query("fileName") chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal")) file, err := breakpointContinueUsecase.FindOrCreateFile(c.Request.Context(), fileMd5, fileName, chunkTotal) if err != nil { response.FailWithMessage("查找失败: "+err.Error(), c) return } response.OkWithDetailed(gin.H{"file": toFileResponse(file)}, "查找成功", c) } // BreakpointContinueFinish // @Tags ExaFileUploadAndDownload // @Summary 创建文件 // @Security ApiKeyAuth // @accept multipart/form-data // @Produce application/json // @Param fileMd5 query string true "文件MD5" // @Param fileName query string true "文件名" // @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建文件,返回包括文件路径" // @Router /fileUploadAndDownload/findFile [post] func (f *FileUploadApi) BreakpointContinueFinish(c *gin.Context) { fileMd5 := c.Query("fileMd5") fileName := c.Query("fileName") filePath, err := utils.MakeFile(fileName, fileMd5) if err != nil { response.FailWithDetailed(gin.H{"filePath": filePath}, "文件创建失败", c) return } response.OkWithDetailed(gin.H{"filePath": filePath}, "文件创建成功", c) } // RemoveChunk // @Tags ExaFileUploadAndDownload // @Summary 删除切片 // @Security ApiKeyAuth // @accept application/json // @Produce application/json // @Param data body map[string]string true "删除缓存切片" // @Success 200 {object} response.Response{msg=string} "删除切片" // @Router /fileUploadAndDownload/removeChunk [post] func (f *FileUploadApi) RemoveChunk(c *gin.Context) { var req struct { FileMd5 string `json:"fileMd5"` FilePath string `json:"filePath"` } if err := c.ShouldBindJSON(&req); err != nil { response.FailWithMessage(err.Error(), c) return } // 路径穿越拦截 if strings.Contains(req.FilePath, "..") || strings.Contains(req.FilePath, "../") || strings.Contains(req.FilePath, "./") || strings.Contains(req.FilePath, ".\\") { response.FailWithMessage("非法路径,禁止删除", c) return } if err := utils.RemoveChunk(req.FileMd5); err != nil { response.FailWithMessage("缓存切片删除失败: "+err.Error(), c) return } if err := breakpointContinueUsecase.DeleteFileChunk(c.Request.Context(), req.FileMd5, req.FilePath); err != nil { response.FailWithMessage(err.Error(), c) return } response.OkWithMessage("缓存切片删除成功", c) } func toFileResponse(file *example.ExaFile) map[string]interface{} { chunks := make([]map[string]interface{}, len(file.ExaFileChunk)) for i, chunk := range file.ExaFileChunk { chunks[i] = map[string]interface{}{ "ID": chunk.ID, "exaFileID": chunk.ExaFileID, "fileChunkNumber": chunk.FileChunkNumber, "fileChunkPath": chunk.FileChunkPath, } } return map[string]interface{}{ "ID": file.ID, "fileName": file.FileName, "fileMd5": file.FileMd5, "filePath": file.FilePath, "chunkTotal": file.ChunkTotal, "isFinish": file.IsFinish, "exaFileChunk": chunks, } }