upload.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package upload
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "kpt-pasture/config"
  7. "kpt-pasture/http/middleware"
  8. "net/http"
  9. "os"
  10. "path/filepath"
  11. "time"
  12. "gitee.com/xuyiping_admin/pkg/apierr"
  13. "gitee.com/xuyiping_admin/pkg/xerr"
  14. "github.com/gin-gonic/gin"
  15. "github.com/xuri/excelize/v2"
  16. )
  17. func Photos(c *gin.Context) {
  18. form, err := c.MultipartForm()
  19. if err != nil {
  20. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("No multipartForm: %s", err.Error()))
  21. return
  22. }
  23. files := form.File["uploads"]
  24. // 验证文件数量
  25. if len(files) == 0 {
  26. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("No files selected"))
  27. return
  28. }
  29. res, err := middleware.BackendOperation(c).OpsService.Photos(c, files)
  30. if err != nil {
  31. apierr.AbortBadRequest(c, http.StatusBadRequest, err)
  32. return
  33. }
  34. c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "Msg": "ok", "data": res})
  35. }
  36. func Files(c *gin.Context) {
  37. // 获取上传的文件
  38. file, err := c.FormFile("file")
  39. if err != nil {
  40. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("获取文件失败: %s", err.Error()))
  41. return
  42. }
  43. // 检查文件类型
  44. ext := filepath.Ext(file.Filename)
  45. if ext != ".xlsx" && ext != ".xls" {
  46. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("只支持Excel文件(.xlsx, .xls)"))
  47. return
  48. }
  49. // 检查文件大小
  50. if file.Size == 0 {
  51. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件为空"))
  52. return
  53. }
  54. // 创建保存目录
  55. uploadDir := fmt.Sprintf("%s/files/excel", config.WorkDir)
  56. if err := os.MkdirAll(uploadDir, 0755); err != nil {
  57. apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("创建目录失败: %s", err.Error()))
  58. return
  59. }
  60. // 生成唯一文件名
  61. filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
  62. filePath := filepath.Join(uploadDir, filename)
  63. // 保存文件
  64. if err := c.SaveUploadedFile(file, filePath); err != nil {
  65. apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("保存文件失败: %s", err.Error()))
  66. return
  67. }
  68. // 打开上传的文件
  69. src, err := file.Open()
  70. if err != nil {
  71. apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("打开文件失败: %s", err.Error()))
  72. return
  73. }
  74. defer src.Close()
  75. // 读取文件内容
  76. fileBytes, err := io.ReadAll(src)
  77. if err != nil {
  78. apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取文件内容失败: %s", err.Error()))
  79. return
  80. }
  81. // 检查文件头部签名
  82. if len(fileBytes) < 8 {
  83. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件格式不正确"))
  84. return
  85. }
  86. // 检查Excel文件签名
  87. isExcel := false
  88. if ext == ".xlsx" {
  89. // XLSX文件签名
  90. if bytes.Equal(fileBytes[:4], []byte{0x50, 0x4B, 0x03, 0x04}) {
  91. isExcel = true
  92. }
  93. } else if ext == ".xls" {
  94. // XLS文件签名
  95. if bytes.Equal(fileBytes[:8], []byte{0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1}) {
  96. isExcel = true
  97. }
  98. }
  99. if !isExcel {
  100. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件格式不正确,请确保上传的是有效的Excel文件"))
  101. return
  102. }
  103. // 读取Excel文件
  104. options := excelize.Options{
  105. RawCellValue: true,
  106. }
  107. f, err := excelize.OpenReader(bytes.NewReader(fileBytes), options)
  108. if err != nil {
  109. // 如果直接读取失败,尝试从保存的文件读取
  110. f, err = excelize.OpenFile(filePath)
  111. if err != nil {
  112. apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取Excel文件失败,请确保文件格式正确: %s", err.Error()))
  113. return
  114. }
  115. }
  116. defer f.Close()
  117. // 获取第一个工作表
  118. sheetName := f.GetSheetName(0)
  119. rows, err := f.GetRows(sheetName)
  120. if err != nil {
  121. apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取工作表失败: %s", err.Error()))
  122. return
  123. }
  124. // 处理Excel数据
  125. if len(rows) < 2 {
  126. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("Excel文件数据为空"))
  127. return
  128. }
  129. // 获取表头并转换为中文
  130. headers := rows[0]
  131. headerMap := make(map[string]string)
  132. for _, header := range headers {
  133. headerMap[header] = header
  134. }
  135. // 处理数据行
  136. var data []map[string]string
  137. for i, row := range rows[1:] {
  138. if len(row) != len(headers) {
  139. apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("第%d行数据列数与表头不匹配", i+2))
  140. return
  141. }
  142. rowData := make(map[string]string)
  143. for j, cell := range row {
  144. rowData[headers[j]] = cell
  145. }
  146. data = append(data, rowData)
  147. }
  148. // 调用后端服务处理数据
  149. if err := middleware.BackendOperation(c).OpsService.ImportExcel(c, data); err != nil {
  150. apierr.AbortBadRequest(c, http.StatusBadRequest, err)
  151. return
  152. }
  153. c.JSON(http.StatusOK, gin.H{
  154. "code": http.StatusOK,
  155. "msg": "导入成功",
  156. "data": nil,
  157. })
  158. }
  159. func OssVideo(c *gin.Context) {
  160. filename := c.Param("name")
  161. videoPath := fmt.Sprintf("%s/files/video/%s", config.WorkDir, filename)
  162. // 打开视频文件
  163. file, err := os.Open(videoPath)
  164. if err != nil {
  165. c.JSON(http.StatusNotFound, gin.H{"error": "Video not found"})
  166. return
  167. }
  168. defer file.Close()
  169. fileInfo, err := file.Stat()
  170. if err != nil {
  171. c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not get file info"})
  172. return
  173. }
  174. // 设置响应头
  175. c.Header("Content-Type", "video/mp4")
  176. c.Header("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
  177. // 流式传输文件内容
  178. http.ServeContent(c.Writer, c.Request, fileInfo.Name(), fileInfo.ModTime(), file)
  179. }