This commit is contained in:
2025-07-07 20:11:59 +08:00
parent ab0fdbc447
commit 06e3aa2eb3
2009 changed files with 193082 additions and 0 deletions

View File

@ -0,0 +1,621 @@
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
}