初始
This commit is contained in:
621
api/v1/common/coryCommon/zip.go
Normal file
621
api/v1/common/coryCommon/zip.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user