622 lines
16 KiB
Go
622 lines
16 KiB
Go
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
|
||
}
|