package controller import ( "encoding/base64" "fmt" "net/url" "path/filepath" "strings" "be.ems/src/framework/config" "be.ems/src/framework/constants/uploadsubpath" "be.ems/src/framework/i18n" "be.ems/src/framework/utils/ctx" "be.ems/src/framework/utils/file" "be.ems/src/framework/vo/result" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" ) // 实例化控制层 FileController 结构体 var NewFile = &FileController{} // 文件操作处理 // // PATH / type FileController struct{} // 下载文件 // // GET /download/:filePath func (s *FileController) Download(c *gin.Context) { language := ctx.AcceptLanguage(c) filePath := c.Param("filePath") if len(filePath) < 8 { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // base64解析出地址 decodedBytes, err := base64.StdEncoding.DecodeString(filePath) if err != nil { c.JSON(400, result.CodeMsg(400, err.Error())) return } routerPath := string(decodedBytes) // 断点续传 headerRange := c.GetHeader("Range") resultMap, err := file.ReadUploadFileStream(routerPath, headerRange) if err != nil { c.JSON(200, result.ErrMsg(err.Error())) return } // 响应头 c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+url.QueryEscape(filepath.Base(routerPath))+`"`) c.Writer.Header().Set("Accept-Ranges", "bytes") c.Writer.Header().Set("Content-Type", "application/octet-stream") if headerRange != "" { c.Writer.Header().Set("Content-Range", fmt.Sprint(resultMap["range"])) c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["chunkSize"])) c.Status(206) } else { c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["fileSize"])) c.Status(200) } c.Writer.Write(resultMap["data"].([]byte)) } // 上传文件 // // POST /upload func (s *FileController) Upload(c *gin.Context) { language := ctx.AcceptLanguage(c) // 上传的文件 formFile, err := c.FormFile("file") if err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 子路径 subPath := c.PostForm("subPath") if _, ok := uploadsubpath.UploadSubpath[subPath]; !ok { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 上传文件转存 upFilePath, err := file.TransferUploadFile(formFile, subPath, nil) if err != nil { c.JSON(200, result.ErrMsg(err.Error())) return } newFileName := upFilePath[strings.LastIndex(upFilePath, "/")+1:] c.JSON(200, result.OkData(map[string]string{ "url": "//" + c.Request.Host + upFilePath, "fileName": upFilePath, "newFileName": newFileName, "originalFileName": formFile.Filename, })) } // 切片文件检查 // // POST /chunkCheck func (s *FileController) ChunkCheck(c *gin.Context) { language := ctx.AcceptLanguage(c) var body struct { // 唯一标识 Identifier string `json:"identifier" binding:"required"` // 文件名 FileName string `json:"fileName" binding:"required"` } err := c.ShouldBindJSON(&body) if err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 读取标识目录 chunks, err := file.ChunkCheckFile(body.Identifier, body.FileName) if err != nil { c.JSON(200, result.ErrMsg(err.Error())) return } c.JSON(200, result.OkData(chunks)) } // 切片文件合并 // // POST /chunkMerge func (s *FileController) ChunkMerge(c *gin.Context) { language := ctx.AcceptLanguage(c) var body struct { // 唯一标识 Identifier string `json:"identifier" binding:"required"` // 文件名 FileName string `json:"fileName" binding:"required"` // 子路径类型 SubPath string `json:"subPath" binding:"required"` } err := c.ShouldBindJSON(&body) if err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } if _, ok := uploadsubpath.UploadSubpath[body.SubPath]; !ok { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 切片文件合并 mergeFilePath, err := file.ChunkMergeFile(body.Identifier, body.FileName, body.SubPath) if err != nil { c.JSON(200, result.ErrMsg(err.Error())) return } newFileName := mergeFilePath[strings.LastIndex(mergeFilePath, "/")+1:] c.JSON(200, result.OkData(map[string]string{ "url": "//" + c.Request.Host + mergeFilePath, "fileName": mergeFilePath, "newFileName": newFileName, "originalFileName": body.FileName, })) } // 切片文件上传 // // POST /chunkUpload func (s *FileController) ChunkUpload(c *gin.Context) { language := ctx.AcceptLanguage(c) // 切片编号 index := c.PostForm("index") // 切片唯一标识 identifier := c.PostForm("identifier") // 上传的文件 formFile, err := c.FormFile("file") if index == "" || identifier == "" || err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 上传文件转存 chunkFilePath, err := file.TransferChunkUploadFile(formFile, index, identifier) if err != nil { c.JSON(200, result.ErrMsg(err.Error())) return } c.JSON(206, result.OkData(chunkFilePath)) } // 转存指定对应文件到静态目录 // // POST /transferStaticFile func (s *CommontController) TransferStaticFile(c *gin.Context) { language := ctx.AcceptLanguage(c) var body struct { UploadPath string `json:"uploadPath" binding:"required"` StaticPath string `json:"staticPath" binding:"required"` Language string `json:"language" binding:"required"` } if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 取语言前缀 lang := strings.SplitN(body.Language, "_", 2)[0] // 默认静态资源 static := config.Get("staticFile.default").(map[string]any) dir, err := filepath.Abs(static["dir"].(string)) if err != nil { c.JSON(400, result.CodeMsg(400, err.Error())) return } delPrefix := strings.Replace(body.StaticPath, static["prefix"].(string), "", 1) staticPath := strings.Replace(delPrefix, "{language}", lang, 1) newFile := filepath.ToSlash(fmt.Sprintf("%s%s", dir, staticPath)) err = file.CopyUploadFile(body.UploadPath, newFile) if err != nil { c.JSON(400, result.CodeMsg(400, err.Error())) return } urlPath := strings.Replace(newFile, dir, static["prefix"].(string), 1) c.JSON(200, result.OkData(filepath.ToSlash(urlPath))) }