kra/internal/server/handler/example/exa_breakpoint_continue.go

177 lines
5.9 KiB
Go

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,
}
}