package upload import ( "bytes" "fmt" "io" "kpt-pasture/config" "kpt-pasture/http/middleware" "net/http" "os" "path/filepath" "time" "gitee.com/xuyiping_admin/pkg/apierr" "gitee.com/xuyiping_admin/pkg/xerr" "github.com/gin-gonic/gin" "github.com/xuri/excelize/v2" ) func Photos(c *gin.Context) { form, err := c.MultipartForm() if err != nil { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("No multipartForm: %s", err.Error())) return } files := form.File["uploads"] // 验证文件数量 if len(files) == 0 { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("No files selected")) return } res, err := middleware.BackendOperation(c).OpsService.Photos(c, files) if err != nil { apierr.AbortBadRequest(c, http.StatusBadRequest, err) return } c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "Msg": "ok", "data": res}) } func Files(c *gin.Context) { // 获取上传的文件 file, err := c.FormFile("file") if err != nil { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("获取文件失败: %s", err.Error())) return } // 检查文件类型 ext := filepath.Ext(file.Filename) if ext != ".xlsx" && ext != ".xls" { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("只支持Excel文件(.xlsx, .xls)")) return } // 检查文件大小 if file.Size == 0 { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件为空")) return } // 创建保存目录 uploadDir := fmt.Sprintf("%s/files/excel", config.WorkDir) if err := os.MkdirAll(uploadDir, 0755); err != nil { apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("创建目录失败: %s", err.Error())) return } // 生成唯一文件名 filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext) filePath := filepath.Join(uploadDir, filename) // 保存文件 if err := c.SaveUploadedFile(file, filePath); err != nil { apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("保存文件失败: %s", err.Error())) return } // 打开上传的文件 src, err := file.Open() if err != nil { apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("打开文件失败: %s", err.Error())) return } defer src.Close() // 读取文件内容 fileBytes, err := io.ReadAll(src) if err != nil { apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取文件内容失败: %s", err.Error())) return } // 检查文件头部签名 if len(fileBytes) < 8 { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件格式不正确")) return } // 检查Excel文件签名 isExcel := false if ext == ".xlsx" { // XLSX文件签名 if bytes.Equal(fileBytes[:4], []byte{0x50, 0x4B, 0x03, 0x04}) { isExcel = true } } else if ext == ".xls" { // XLS文件签名 if bytes.Equal(fileBytes[:8], []byte{0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1}) { isExcel = true } } if !isExcel { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件格式不正确,请确保上传的是有效的Excel文件")) return } // 读取Excel文件 options := excelize.Options{ RawCellValue: true, } f, err := excelize.OpenReader(bytes.NewReader(fileBytes), options) if err != nil { // 如果直接读取失败,尝试从保存的文件读取 f, err = excelize.OpenFile(filePath) if err != nil { apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取Excel文件失败,请确保文件格式正确: %s", err.Error())) return } } defer f.Close() // 获取第一个工作表 sheetName := f.GetSheetName(0) rows, err := f.GetRows(sheetName) if err != nil { apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取工作表失败: %s", err.Error())) return } // 处理Excel数据 if len(rows) < 2 { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("Excel文件数据为空")) return } // 获取表头并转换为中文 headers := rows[0] headerMap := make(map[string]string) for _, header := range headers { headerMap[header] = header } // 处理数据行 var data []map[string]string for i, row := range rows[1:] { if len(row) != len(headers) { apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("第%d行数据列数与表头不匹配", i+2)) return } rowData := make(map[string]string) for j, cell := range row { rowData[headers[j]] = cell } data = append(data, rowData) } // 调用后端服务处理数据 if err := middleware.BackendOperation(c).OpsService.ImportExcel(c, data); err != nil { apierr.AbortBadRequest(c, http.StatusBadRequest, err) return } c.JSON(http.StatusOK, gin.H{ "code": http.StatusOK, "msg": "导入成功", "data": nil, }) } func OssVideo(c *gin.Context) { filename := c.Param("name") videoPath := fmt.Sprintf("%s/files/video/%s", config.WorkDir, filename) // 打开视频文件 file, err := os.Open(videoPath) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Video not found"}) return } defer file.Close() fileInfo, err := file.Stat() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not get file info"}) return } // 设置响应头 c.Header("Content-Type", "video/mp4") c.Header("Content-Length", fmt.Sprintf("%d", fileInfo.Size())) // 流式传输文件内容 http.ServeContent(c.Writer, c.Request, fileInfo.Name(), fileInfo.ModTime(), file) }