package controllers import ( "fmt" "io" "log" "mime/multipart" "net/http" "os" "path/filepath" "strings" "time" "xugu_license/internal/global" "xugu_license/internal/models" "xugu_license/internal/module/xlsx" "xugu_license/internal/utils" "github.com/gin-gonic/gin" ) type fileInfo struct { fileName string fileSize int64 fileMd5 string buf *os.File mf *multipart.File } func UploadfileHandler(c *gin.Context) { userAny, exists := c.Get("userInfo") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) c.Abort() return } userInfo := userAny.(*models.UserInfo) //接收文件 //检查文件 file, err := checkUploadFile(c) if err != nil { global.Logger.Errorln("检测上传文件不为xlsx格式 ", err.Error()) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintln("检测上传文件不为xlsx格式: ", err.Error())}) return } //保存文件 saveFile(file) //解析文件 lics, err := xlsx.XlsxController(file.mf) if err != nil { global.Logger.Errorln("解析文件失败 ", err.Error()) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintln("解析文件失败: ", err.Error())}) return } //插入数据到数据库 for _, lic := range lics { err = models.InsertLicenseApplicationAndInfoRow(lic, userInfo.Id, userInfo.Username) if err != nil { global.Logger.Errorln("插入数据失败 ", err.Error()) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintln("插入数据失败: ", err.Error())}) return } } //生成license // for _, lic := range lics { // pI := license.ProjectInfo{ // ProjectName: lic.ProductName, // UserName: lic.AssociatedProject, // UserAddr: "", // SerialNumber: "", // } // eI := license.EnvironmentInfo{ // CpuSN: "", // BaseboardSN: "", // MacAddr: lic.MasterMacAddress, // DiskID: "", // IPAddr: "", // } // //判断license类型 // LicType := utils.SwitchLicenseType(lic.Version) // lI := license.LicenseInfo{ // GenDate: "2024-07-15", // ExpireDate: "9999-12-31", // LicenseType: LicType, // LicenseVersion: 1, // HardType: 3, // } // licStr := license.GenerateLicense(pI, eI, lI) // fmt.Println("生成的license为:", licStr) // //生成副主节点license // var licStr2 []byte // if lic.SecondaryMasterMacAddress != "" { // eI.MacAddr = lic.SecondaryMasterMacAddress // licStr2 = license.GenerateLicense(pI, eI, lI) // } // xlsx.ExcelToMail(lic, licStr, licStr2) // } file.buf.Close() } // UploadFile 处理文件上传 func checkUploadFile(c *gin.Context) (*fileInfo, error) { // 获取上传的文件 file, err := c.FormFile("file") if err != nil { global.Logger.Errorln("未收到文件 ", err.Error()) c.JSON(http.StatusBadRequest, gin.H{"error": "服务器未收到文件"}) return nil, err } // 检查文件扩展名是否为xlsx ext := strings.ToLower(filepath.Ext(file.Filename)) if ext != ".xlsx" { global.Logger.Errorln("文件格式不为xlsx ", err.Error()) c.JSON(http.StatusBadRequest, gin.H{"error": "Only .xlsx files are allowed"}) return nil, err } // 获取客户端发送的 MD5 值 clientMd5 := c.PostForm("md5") if clientMd5 == "" { global.Logger.Errorln("md5不存在 ") c.JSON(http.StatusBadRequest, gin.H{"error": "MD5 value is required"}) return nil, err } // 计算文件的 MD5 值 fileMd5, err := utils.CalculateFileHeaderMd5(file) if err != nil { global.Logger.Errorln("计算文件md5失败 ", err.Error()) c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to calculate MD5"}) return nil, err } // 比较 MD5 值 if fileMd5 != clientMd5 { // 删除保存的文件 //os.Remove(filePath) global.Logger.Errorln("文件md5不一致 ") c.JSON(http.StatusBadRequest, gin.H{"error": "MD5 mismatch"}) return nil, err } // 将 *multipart.FileHeader 转换为 *os.File tempFile, err := fileHeaderToOsFile(file) if err != nil { global.Logger.Errorln(" *multipart.FileHeader 转换为 *os.File 失败") c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to convert uploaded file to *os.File"}) return nil, err } //defer tempFile.Close() c.JSON(http.StatusOK, gin.H{"message": "File uploaded successfully", "file": file.Filename}) src, err := file.Open() if err != nil { global.Logger.Errorln("src, err := file.Open()失败 ", err.Error()) return nil, err } return &fileInfo{fileName: file.Filename, fileSize: file.Size, fileMd5: fileMd5, buf: tempFile, mf: &src}, nil } // 保存文件 func saveFile(file *fileInfo) error { // 确保保存文件的目录存在 saveDir := "files" if err := os.MkdirAll(saveDir, os.ModePerm); err != nil { log.Fatal(err) } // 生成新的文件名 originalFileName := file.fileName ext := filepath.Ext(originalFileName) name := strings.TrimSuffix(originalFileName, ext) timestamp := time.Now().Format("20060102_150405") newFileName := utils.GenerateFileName(name, timestamp, ext) filePath := filepath.Join(saveDir, newFileName) // 保存文件 // 创建目标文件 dst, err := os.Create(filePath) if err != nil { global.Logger.Errorln("创建文件失败", err.Error()) return err } defer dst.Close() // 将内容从 src 复制到 dst fmt.Println("file.buf", file.buf) if _, err := io.Copy(dst, file.buf); err != nil { global.Logger.Errorln("内容从 src 复制到 dst", err.Error()) return err } return nil } // fileHeaderToOsFile 将 *multipart.FileHeader 转换为 *os.File func fileHeaderToOsFile(fileHeader *multipart.FileHeader) (*os.File, error) { src, err := fileHeader.Open() if err != nil { global.Logger.Errorln("*multipart.FileHeader 转换为 *os.File失败", err.Error()) return nil, err } defer src.Close() // 创建临时文件 tempFile, err := os.CreateTemp("", "upload-*.xlsx") if err != nil { global.Logger.Errorln("创建临时文件失败", err.Error()) return nil, err } // 将内容从 src 复制到 tempFile if _, err := io.Copy(tempFile, src); err != nil { global.Logger.Errorln("内容从 src 复制到 tempFile失败", err.Error()) tempFile.Close() return nil, err } // 将文件指针移回文件开头 if _, err := tempFile.Seek(0, 0); err != nil { tempFile.Close() global.Logger.Errorln("将文件指针移回文件开头失败", err.Error()) return nil, err } return tempFile, nil }