package util import ( "archive/zip" "aufs/config" "bytes" "crypto/sha256" "encoding/base64" "fmt" "io" "mime/multipart" "net/http" "os" "path" "path/filepath" "strings" "github.com/schollz/progressbar/v3" ) // 遍历目录下的文件 // 是否只扫相对路径下 func GetDirFilePaths(dirPath string, relativeOnly bool) ([]string, error) { var filePaths []string err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { if relativeOnly { fileName := filepath.Base(path) relativePath := filepath.ToSlash(filepath.Join(filepath.Base(dirPath), fileName)) filePaths = append(filePaths, relativePath) } else { filePaths = append(filePaths, filepath.ToSlash(path)) } } return nil }) if err != nil { return nil, err } return filePaths, nil } // base64 encode // url safe func Bas64end(str string) string { // bdata:= return base64.URLEncoding.EncodeToString([]byte(str)) } // base64 url safe uneconde func Base64dec(bsstr string) string { dedc, _ := base64.URLEncoding.DecodeString(bsstr) return string(dedc) } // 计算文件的hash func CalacHash(rfile string) string { // 获取到真实地址 // file, err := os.Open(rfile) if err != nil { panic(err) } defer file.Close() // initlize hash object hash := sha256.New() // write file into hash object if _, err := io.Copy(hash, file); err != nil { panic(err) } // get hash value hashBytes := hash.Sum(nil) //converto to hash string hastString := fmt.Sprintf("%x", hashBytes) return hastString } // 判断文件是否存在 func IsFileExist(filename string) bool { if _, err := os.Stat(filename); err == nil { return true } return false } // 发送文件 func SendFiles(filePath string, url string) error { filePath = filepath.ToSlash(filePath) fileInfo, err := os.Stat(filePath) if err != nil { if os.IsNotExist(err) { return fmt.Errorf("file [%s] not exist", filePath) } return fmt.Errorf("file [%s] error: %w", filePath, err) } if fileInfo.IsDir() { return postDirectory(filePath, url) } return postFile(filePath, path.Base(filePath), url) } // 传送文件夹 func postDirectory(dirPath string, url string) error { files, err := GetDirFilePaths(dirPath, false) if err != nil { return err } fmt.Println("\nAll files in folder:") for _, file := range files { fmt.Println(file) } var confirm string fmt.Print("\nTransfer all files? [Y/N] ") fmt.Scanln(&confirm) if strings.ToLower(confirm) != "y" { fmt.Print("\nCancel send all files ") return nil } for _, file := range files { fileName, _ := filepath.Rel(dirPath, file) fileName = filepath.Join(filepath.Base(dirPath), fileName) err := postFile(file, fileName, url) if err != nil { return err } } fmt.Printf("Send folder %s success.\n", dirPath) return nil } // 传送文件 func postFile(wfpath string, filename string, url string) error { payload := &bytes.Buffer{} writer := multipart.NewWriter(payload) file, err := os.Open(wfpath) if err != nil { return err } defer file.Close() part, err := writer.CreateFormFile("file", filepath.ToSlash(filename)) if err != nil { return err } fileInfo, _ := file.Stat() bar := progressbar.DefaultBytes( fileInfo.Size(), fmt.Sprintf("Uploading [%s]", filename), ) _, err = io.Copy(io.MultiWriter(part, bar), file) if err != nil { return err } err = writer.Close() if err != nil { return err } req, err := http.NewRequest(http.MethodPost, url, payload) if err != nil { return err } req.Header.Set("Content-Type", writer.FormDataContentType()) // 在头里面加上路径,去除前缀 rlpath := strings.TrimPrefix(wfpath, config.G.FilePath) // fmt.Printf("wfpath:%s,rel path is:%s", wfpath, rlpath) req.Header.Set("Fpath", Bas64end(rlpath)) client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("upload failed with status code: %d", resp.StatusCode) } return nil } /* * add file to zip */ func ZipFiles(zipname string, files []string, zippath string) error { fmt.Printf("zipname:%s\n", zipname) // 创建zip文件 newZipFile, err := os.Create(zipname) if err != nil { return err } defer newZipFile.Close() // zip写头 zipWriter := zip.NewWriter(newZipFile) defer zipWriter.Close() // 把files添加到zip中 for _, file := range files { // fmt.Printf("zipfiles in function :%s\n", file) // zipfile, err := os.Open(file) if err != nil { return err } defer zipfile.Close() // info, err := zipfile.Stat() if err != nil { return err } // header, err := zip.FileInfoHeader(info) if err != nil { return err } // 在zip存档中设置文件的相对路径 header.Name, err = filepath.Rel(filepath.Dir(config.G.FilePath), file) if err != nil { return err } // 转换为Unix风格的路径分隔符 header.Name = filepath.ToSlash(header.Name) // 目录需要拼上一个 "/" ,否则会出现一个和目录一样的文件在压缩包中 if info.IsDir() { header.Name += "/" } else { // 将压缩方法设置为deflate header.Method = zip.Deflate } // fmt.Printf("header.name is %s\n", header.Name) writer, err := zipWriter.CreateHeader(header) if err != nil { return err } if _, err = io.Copy(writer, zipfile); err != nil { return err } } return nil } // 解压缩zip文件 // 这个方法必须是首字母大写的,才能被注册 // src: 需要解压的zip文件 func DecompressZip(zpFname string) error { // 下载文件 archive, err := zip.OpenReader(zpFname) if err != nil { return err } // 取zip文件的绝对路径的文件夹。会默认解压到files下,暂停使用 // dir := filepath.Dir(zpFname) defer archive.Close() // 遍历目录 for _, f := range archive.File { // zip 解压的时候,路径为监听的路径 filePath := filepath.Join(config.G.FilePath, f.Name) // 暂停原因 屏蔽解压到files 目录下 //filePath := filepath.Join(dir, f.Name) if f.FileInfo().IsDir() { os.MkdirAll(filePath, os.ModePerm) continue } // 父文件夹开始创建目录 if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { return fmt.Errorf("failed to make directory (%v)", err) } // 文件原有模式 dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return fmt.Errorf("failed to create file (%v)", err) } fileInArchive, err := f.Open() if err != nil { return fmt.Errorf("failed to open file in zip (%v)", err) } if _, err := io.Copy(dstFile, fileInArchive); err != nil { return fmt.Errorf("failed to copy file in zip (%v)", err) } dstFile.Close() fileInArchive.Close() } return nil } // dest:目的地址以及压缩文件名 eg:/data/logZip/2022-12-12-log.zip // paths:需要压缩的所有文件所组成的集合 func CompressToZip(dest string, currentPath string, paths []string) error { // fmt.Println("real path", currentPath) // 打开files 目录 filesPath := "" if dir, err := os.Getwd(); err == nil { filesPath = dir + "/sync_zips/" } if err := os.MkdirAll(filesPath, os.ModePerm); err != nil { fmt.Println(err.Error()) } // ToSlash 过滤windows的斜杠引起的bug zfile, err := os.Create(path.Join("./sync_zips", "/", dest)) if err != nil { return err } defer zfile.Close() zipWriter := zip.NewWriter(zfile) defer zipWriter.Close() // 遍历带压缩的目录信息 for _, src := range paths { // 替换文件名,并且去除前后 "\" 或 "/" // path = strings.Trim(path, string(filepath.Separator)) // 从配置中读取到源目录 src = path.Join(currentPath, "/", src) // 删除尾随路径(如果它是目录) src := strings.TrimSuffix(src, string(os.PathSeparator)) // 文件遍历 err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // 创建本地文件头 header, err := zip.FileInfoHeader(info) if err != nil { return err } // 监听的工作目录作为文件的结尾,然后取相对路径 // 在zip存档中设置文件的相对路径 header.Name, err = filepath.Rel(filepath.Dir(config.G.FilePath), path) if err != nil { return err } // 转换为Unix风格的路径分隔符 header.Name = filepath.ToSlash(header.Name) // 目录需要拼上一个 "/" ,否则会出现一个和目录一样的文件在压缩包中 if info.IsDir() { header.Name += "/" } else { // 将压缩方法设置为deflate header.Method = zip.Deflate } // fmt.Printf("zip header:%v\n", header.Name) // 创建写入头的writer headerWriter, err := zipWriter.CreateHeader(header) if err != nil { return err } if info.IsDir() { return nil } f, err := os.Open(path) if err != nil { return err } defer f.Close() _, err = io.Copy(headerWriter, f) return err }) if err != nil { return err } } // 存到指定位置 os.Rename(dest, path.Join("./sync_zips/", dest)) return nil }