package coryCommon import ( "archive/zip" "bytes" "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" "io" "io/ioutil" "os" "path/filepath" "strings" ) //解压文件、文件夹复制 // FileZipFunc 对文件进行解压 func FileZipFunc(relativePath string, filenPath string, template string) (fileName string, err error) { // 打开压缩文件 zipFile, err := zip.OpenReader(relativePath) if err != nil { fmt.Println("无法打开压缩文件(只支持ZIP):", err) return } var i = 0 var name = "" //进入的压缩文件,判断压缩文件中的第一层是否包含文件,如果有就在当前随机产生一个文件夹,返回就返回到随机文件夹的位置 randomFolder := "" // 遍历压缩文件中的文件头信息 for _, f := range zipFile.File { //path, _ := gbkDecode(f.Name) fmt.Println("头? ", f.Name) path, _ := IsGB18030(f.Name) // 判断是否为顶层文件夹 if !hasSlash(path) { randomFolder = FileName("/randomFolder") template = template + randomFolder filenPath = filenPath + randomFolder break } } // 遍历压缩文件中的文件 for _, file := range zipFile.File { fmt.Println("ti ? ", file.Name) // 解决文件名编码问题 if i == 0 { gb18030, _ := IsGB18030(file.Name) if err != nil { return "", err } name = gb18030 } file.Name, err = IsGB18030(file.Name) if err != nil { return "", err } //对所有文件的名称进行空格替换 file.Name = strings.Replace(file.Name, " ", "", -1) if err != nil { return file.Name, err } // 获取文件的相对路径,根据原本路径来操作还是根据新路径来(filenPath) extractedFilePath := "" extractedFilePathTwo := "" if filenPath != "" { extractedFilePath = filepath.Join(FileToFunc(filenPath, 2), "/"+file.Name) split := strings.Split(filenPath, "/") extractedFilePathTwo = extractedFilePath + "/" + split[len(split)-1] } else { extractedFilePath = filepath.ToSlash(filepath.Join(GetCWD()+template+"/"+".", file.Name)) extractedFilePathTwo = extractedFilePath + "/" } //判断文件夹是否存在,存在就退出(i==0 是为了只判断最外层那一文件夹路径) _, err := os.Stat(filepath.Dir(extractedFilePathTwo)) if err == nil && i == 0 { zipFile.Close() // 删除压缩文件 err = os.Remove(relativePath) err = errors.New("当前文件夹已经存在,导入无效!") return "", err } i = i + 1 // 检查是否为文件 if !file.FileInfo().IsDir() { // 创建文件的目录结构 err = os.MkdirAll(filepath.Dir(extractedFilePath), os.ModePerm) if err != nil { fmt.Println("无法创建目录:", err) return "", err } // 打开压缩文件中的文件 zippedFile, err := file.Open() if err != nil { fmt.Println("无法打开压缩文件中的文件:", err) return "", err } defer zippedFile.Close() // 创建目标文件 extractedFile, err := os.Create(extractedFilePath) if err != nil { fmt.Println("无法创建目标文件:", err) return "", err } defer extractedFile.Close() // 将压缩文件中的内容复制到目标文件 _, err = io.Copy(extractedFile, zippedFile) if err != nil { fmt.Println("无法解压缩文件:", err) return "", err } } } zipFile.Close() // 删除压缩文件 err = os.Remove(relativePath) if err != nil { fmt.Println("无法删除压缩文件:", err) return } fileName = strings.Split(name, "/")[0] if randomFolder != "" { fileName = "" } else { fileName = "/" + fileName } if filenPath != "" { return FileToFunc(filenPath, 2) + fileName, err } else { return GetCWD() + template + fileName, err } } // IsGB18030 判断字符串是否是 GB18030 编码 func IsGB18030(name string) (string, error) { // 创建 GB18030 解码器 decoder := simplifiedchinese.GB18030.NewDecoder() // 使用 transform 解码数据 _, err := io.ReadAll(transform.NewReader(bytes.NewReader([]byte(name)), decoder)) if err == nil { return name, nil } else { fileName, errName := simplifiedchinese.GB18030.NewDecoder().String(name) return fileName, errName } } // gbkDecode 解决文件名乱码 func gbkDecode(s string) (string, error) { gbkDecoder := simplifiedchinese.GBK.NewDecoder() decodedName, _, err := transform.String(gbkDecoder, s) return decodedName, err } type DocumentListPublicRes struct { Id int64 `json:"id"` IdStr string `json:"idStr"` Pid string `json:"pid"` Name string `json:"name"` FilenPath string `json:"filenPath"` FilenPathCoding string `json:"filenPathCoding"` Suffix string `json:"suffix"` Type string `json:"type"` CreateBy string `json:"createBy"` CreatedAt *gtime.Time `json:"createdAt"` IsDuplicate bool `json:"isDuplicate"` ProjectId int64 `json:"projectId"` } // Traversal 遍历文件夹 ctx,绝对路径,上级文件夹(可有可无),表名,存储位置,项目id,模板1or资料2 // // one, err := coryCommon.Traversal(ctx, path, req.Pid, dao.DocumentCompletion.Table(), dataFolder, req.ProjectId, "2") //遍历解压后的文件,插入数据 // if err != nil { // liberr.ErrIsNil(ctx, err) // return // } // _, err = g.DB().Model(dao.DocumentCompletion.Table()).Ctx(ctx).Insert(one) // liberr.ErrIsNil(ctx, err, "新增失败!") func Traversal(ctx context.Context, root string, pidstr string, tableName string, templatePath string, projectId int64, num string) (dataOne []*DocumentListPublicRes, err error) { template := strings.Replace(templatePath, "/resource/public", "/file", 1) err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // 获取相对路径 relativePath, err := filepath.Rel(root, path) if err != nil { return err } // 根目录下创建,还是指定文件夹下面创建? p := "" if pidstr != "" { value, _ := g.DB().Model(tableName).Ctx(ctx).Where("id_str", pidstr).Fields("filen_path").Value() split := strings.Split(root, "/") p = value.String() + "/" + split[len(split)-1] + "/" + relativePath } else { p = template + "/" + relativePath } p = strings.ReplaceAll(p, "\\", "/") // 获取当前项的深度 depth := strings.Count(relativePath, string(filepath.Separator)) // 判断父子关系并打印结果 if depth == 0 && info.IsDir() { if relativePath == "." { split := strings.Split(root, "/") n := split[len(split)-1] // 根目录下创建,还是指定文件夹下面创建? p := "" if pidstr != "" { value, _ := g.DB().Model(tableName).Ctx(ctx).Where("id_str", pidstr).Fields("filen_path").Value() p = value.String() + "/" + n } else { p = template + "/" + n } p = strings.ReplaceAll(p, "\\", "/") template = template + "/" + n var dataTwo = new(DocumentListPublicRes) dataTwo.IdStr = SHA256(p) if pidstr != "" { dataTwo.Pid = pidstr } else { dataTwo.Pid = "0" } dataTwo.Name = n dataTwo.FilenPath = p ////如果文件夹路径重复,就提示 解压文件夹的时候就已经判断了,这里就不需要了 //err := IsFolderExist(ctx, p) //if err != nil { // return err //} dataTwo.Type = "2" dataOne = append(dataOne, dataTwo) } else { dir, n := filepath.Split(p) dir = strings.TrimSuffix(dir, "/") var dataTwo = new(DocumentListPublicRes) dataTwo.IdStr = SHA256(p) dataTwo.Pid = SHA256(dir) dataTwo.Name = n dataTwo.FilenPath = p ////如果文件夹路径重复,就提示 //err := IsFolderExist(ctx, p) //if err != nil { // return err //} dataTwo.Type = "2" dataOne = append(dataOne, dataTwo) } } else if info.IsDir() { // 子文件夹 dir, n := filepath.Split(p) dir = strings.TrimSuffix(dir, "/") var dataTwo = new(DocumentListPublicRes) dataTwo.IdStr = SHA256(p) dataTwo.Pid = SHA256(dir) dataTwo.Name = n dataTwo.FilenPath = p dataTwo.Type = "2" dataOne = append(dataOne, dataTwo) } else { dir, n := filepath.Split(p) dir = strings.TrimSuffix(dir, "/") var dataTwo = new(DocumentListPublicRes) dataTwo.Pid = SHA256(dir) lastDotIndex := strings.LastIndex(n, ".") if lastDotIndex == -1 || lastDotIndex == 0 { dataTwo.Name = strings.Split(n, ".")[0] } else { dataTwo.Name = n[:lastDotIndex] } dataTwo.Suffix = n[lastDotIndex:] dataTwo.FilenPath = p dataTwo.Type = "1" //文件只能是这三种类型,其他类型进不来 s := n[lastDotIndex:] if num == "1" { //资料有格式限制 if strings.EqualFold(s, ".xls") || strings.EqualFold(s, ".xlsx") || strings.EqualFold(s, ".docx") || strings.EqualFold(s, ".doc") || strings.EqualFold(s, ".pptx") || strings.EqualFold(s, ".ppt") { dataOne = append(dataOne, dataTwo) } } else { dataOne = append(dataOne, dataTwo) } } return err }) if err != nil { //fmt.Println("遍历文件夹时发生错误:", err) return nil, err } // 有项目id表示资料 无表模板 if projectId > 0 { for i := range dataOne { dataOne[i].ProjectId = projectId } } return } func SHA256(str string) (hx string) { // 创建 SHA-256 哈希对象 hash := sha256.New() // 将字符串转换为字节数组并进行哈希计算 hash.Write([]byte(str)) // 计算 SHA-256 哈希值 hashedBytes := hash.Sum(nil) // 将哈希值转换为十六进制字符串 hashStr := hex.EncodeToString(hashedBytes) return hashStr } // CopyFile 文件复制 func CopyFile(src, dst string) error { // 打开源文件 inputFile, err := os.Open(src) if err != nil { return err } defer inputFile.Close() // 创建目标文件 outputFile, err := os.Create(dst) if err != nil { return err } defer outputFile.Close() // 通过 Copy 函数实现拷贝功能 _, err = io.Copy(outputFile, inputFile) if err != nil { return err } // 确保文件内容被刷新到磁盘上 err = outputFile.Sync() if err != nil { return err } return nil } // CopyDirectory 文件夹复制 func CopyDirectory(src string, dest string) error { // 检查源文件夹是否存在 _, err := os.Stat(src) if err != nil { return err } // 检查目标文件夹是否存在,不存在则创建 err = os.MkdirAll(dest, 0755) if err != nil { return err } // 遍历源文件夹 files, err := os.ReadDir(src) if err != nil { return err } for _, file := range files { srcPath := src + "/" + file.Name() destPath := dest + "/" + file.Name() // 判断文件类型 if file.IsDir() { // 如果是文件夹,则递归调用 copyDirectory 函数复制文件夹及其子文件 err = CopyDirectory(srcPath, destPath) if err != nil { return err } } else { // 如果是文件,则复制文件到目标文件夹 inputFile, err := os.Open(srcPath) if err != nil { return err } defer inputFile.Close() outputFile, err := os.Create(destPath) if err != nil { return err } defer outputFile.Close() _, err = io.Copy(outputFile, inputFile) if err != nil { return err } } } return nil } // MoveFile 文件移动 func MoveFile(source, destination string) (err error) { // 执行移动操作 err = os.Rename(source, destination) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("File moved successfully!") } return err } // MoveFolder 文件夹下面的文件及子文件全部移动到新文件夹下 func MoveFolder(srcPath, destPath string) error { // 获取源文件夹下的所有文件和子文件夹 fileList := []string{} err := filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error { fileList = append(fileList, path) return nil }) if err != nil { return err } // 移动每个文件和子文件夹 for _, file := range fileList { // 获取相对路径 relPath, err := filepath.Rel(srcPath, file) if err != nil { return err } // 构建目标路径 destFile := filepath.Join(destPath, relPath) // 判断是文件还是文件夹 if fileInfo, err := os.Stat(file); err == nil && fileInfo.IsDir() { // 如果是文件夹,创建目标文件夹 err := os.MkdirAll(destFile, os.ModePerm) if err != nil { return err } } else { // 如果是文件,复制文件 err := copyFile(file, destFile) if err != nil { return err } } } // 移动完成后删除源文件夹 return os.RemoveAll(srcPath) } func copyFile(src, dest string) error { srcFile, err := os.Open(src) if err != nil { return err } defer srcFile.Close() destFile, err := os.Create(dest) if err != nil { return err } defer destFile.Close() _, err = io.Copy(destFile, srcFile) return err } // FolderToZip 将给定文件夹压缩成压缩包存储到另外一个路径(参数:源数据、目标路径) func FolderToZip(folderToZip, zipFile string) (err error) { // 创建一个新的压缩包文件 newZipFile, err := os.Create(zipFile) if err != nil { return err } defer newZipFile.Close() // 创建一个 zip.Writer 来向压缩包中写入内容 zipWriter := zip.NewWriter(newZipFile) defer zipWriter.Close() // 递归遍历文件夹并将其中的文件和目录添加到压缩包中 err = filepath.Walk(folderToZip, func(filePath string, info os.FileInfo, err error) error { if err != nil { return err } // 获取当前文件的相对路径 relativePath, err := filepath.Rel(folderToZip, filePath) if err != nil { return err } // 如果是目录,则创建一个目录项 if info.IsDir() { _, err = zipWriter.Create(relativePath + "/") if err != nil { return err } return nil } // 如果是文件,则创建一个文件项并写入文件内容 fileData, err := ioutil.ReadFile(filePath) if err != nil { return err } file, err := zipWriter.Create(relativePath) if err != nil { return err } _, err = file.Write(fileData) if err != nil { return err } return nil }) return } // zipDirFiles 压缩文件 func ZipDirFiles(src_dir string, zip_file_name string) error { // 检查并创建目标目录 err := os.MkdirAll(filepath.Dir(zip_file_name), os.ModePerm) if err != nil { return err } // 删除空的zip,而不是直接使用os.RemoveAll,以提高安全性 err = os.Remove(zip_file_name) if err != nil && !os.IsNotExist(err) { return err } // 创建新的zip文件 zipfile, err := os.Create(zip_file_name) if err != nil { return err } defer zipfile.Close() // 初始化zip写入器 archive := zip.NewWriter(zipfile) defer archive.Close() // 遍历源目录下的文件和子目录 return filepath.Walk(src_dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // 计算相对于源目录的路径 relPath, _ := filepath.Rel(src_dir, path) if path == src_dir { // 如果是源目录本身,跳过 return nil } // 创建zip文件头 header, err := zip.FileInfoHeader(info) if err != nil { return err } header.Name = filepath.ToSlash(relPath) // 标记目录 if info.IsDir() { header.Name += "/" return nil } // 处理文件 file, err := os.Open(path) if err != nil { return err } defer file.Close() writer, err := archive.CreateHeader(header) if err != nil { return err } _, err = io.Copy(writer, file) return err }) } // 压缩后删除源文件 func DeleteFolderToZip(srcpath, destpathzip string) (err error) { //srcpath := filepath.ToSlash(`D:\GiteeProject\gsproject\zmkg-back\resource\public\temporary\del`) //destpathzip := filepath.ToSlash(`D:\GiteeProject\gsproject\zmkg-back\resource\public\temporary\yy.zip`) err = ZipDirFiles(srcpath, destpathzip) // 文件压缩 压缩文件目录 和压缩文件zip if err != nil { return err } // 删除原文件 if err := os.RemoveAll(srcpath); err != nil { if !os.IsNotExist(err) { fmt.Printf("Error removing existing file %s: %v\n", srcpath, err) return err } } return err } // 判断字符串中是否包含斜杠 func hasSlash(s string) bool { for _, c := range s { if c == '/' || c == '\\' { return true } } return false }