package util import ( "archive/tar" "basic.com/valib/logger.git" "compress/gzip" "errors" "fmt" "io" "io/ioutil" "log" "os" "os/exec" "path" "path/filepath" "strings" ) //FileName 待压缩的文件名 //DesPathName 压缩完之后存放的目录名 func Gzip(FilePathName, DesPathName string) error { // 清理路径字符串 FilePathName = path.Clean(FilePathName) // 要解压的文件名字 FileName := FilePathName index := strings.LastIndex(FilePathName, "\\") if index != -1 { FileName = FilePathName[index+1:] } logger.Debug("文件名称是", FileName) //创建存放压缩文件的路径(如果文件夹不存在) if !DirExists(DesPathName) { os.MkdirAll(DesPathName, 0777) } logger.Debug("创建文件") //创建压缩文件 DesFileName := DesPathName + "\\" + FileName + ".gz" fw, er := os.Create(DesFileName) if er != nil { return er } defer fw.Close() gw := gzip.NewWriter(fw) defer gw.Close() //读取文件内容 rd := make([]byte, 1024*1024) rd, err := ioutil.ReadFile(FilePathName) if err != nil { logger.Debug("读取文件内的数据出错 err:", err) return err } // 获取文件或目录信息 fi, err := os.Stat(FilePathName) if err != nil { logger.Debug("获取文件的详细信息出错 err :", err) return nil } gw.Name = fi.Name() gw.ModTime = fi.ModTime() _, err = gw.Write(rd) if err != nil { logger.Debug("写入压缩文件出错 err:", err) return err } err = gw.Flush() if err != nil { logger.Debug("gw.Flush() 出错 err:", err) return err } return nil } func UnGz(srcGz string, filePath string) error { fr, err := os.Open(srcGz) if err != nil { logger.Debug("open secGZ failed. err:%v", err) return err } gr, err := gzip.NewReader(fr) if err != nil { fr.Close() logger.Debug("create gzip.reader failed. err:%v", err) return err } index := strings.LastIndex(srcGz, ".") if index == -1 { gr.Close() fr.Close() logger.Debug("find . failed. err:%v", err) return err } fw, err := os.Create(filePath) if err != nil { gr.Close() fr.Close() logger.Debug("create file failed. err:%v", err) return err } // 写文件 _, err = io.Copy(fw, gr) if err != nil { fw.Close() gr.Close() fr.Close() logger.Debug("write file failed. err:%v", err) return err } fw.Close() gr.Close() fr.Close() //删除gz压缩文件 err = os.Remove(srcGz) if err != nil { logger.Debug("remove file failed. err:%v", err) return err } return nil } // 将文件或目录打包成 .tar 文件 // src 是要打包的文件或目录的路径 // dstTar 是要生成的 .tar 文件的路径 // failIfExiBst 标记如果 dstTar 文件存在,是否放弃打包,如果否,则会覆盖已存在的文件 func TarGz(src string, dstTar string, failIfExist bool) error { // 清理路径字符串 src = path.Clean(src) // 判断要打包的文件或目录是否存在 if !Exists(src) { return errors.New("要打包的文件或目录不存在:" + src) } // 判断目标文件是否存在 if FileExists(dstTar) { if failIfExist { // 不覆盖已存在的文件 return errors.New("目标文件已经存在:" + dstTar) } else { // 覆盖已存在的文件 if er := os.Remove(dstTar); er != nil { return er } } } // 创建空的目标文件 fw, er := os.Create(dstTar) if er != nil { return er } defer fw.Close() gw := gzip.NewWriter(fw) defer gw.Close() // 创建 tar.Writer,执行打包操作 tw := tar.NewWriter(gw) var err error defer func() { // 这里要判断 tw 是否关闭成功,如果关闭失败,则 .tar 文件可能不完整 if er := tw.Close(); er != nil { err = er } }() // 获取文件或目录信息 fi, er := os.Stat(src) if er != nil { return er } // 获取要打包的文件或目录的所在位置和名称 srcBase, srcRelative := path.Split(path.Clean(src)) // 开始打包 if fi.IsDir() { logger.Debug("第一层目录:", srcRelative) tarDir(srcBase, srcRelative, tw, fi) } else { tarFile(srcBase, srcRelative, tw, fi) } return nil } // 因为要执行遍历操作,所以要单独创建一个函数 func tarDir(srcBase, srcRelative string, tw *tar.Writer, fi os.FileInfo) (err error) { // 获取完整路径 srcFull := srcBase + srcRelative logger.Debug("srcFull:", srcFull) // 在结尾添加 "/" last := len(srcRelative) - 1 if srcRelative[last] != os.PathSeparator { srcRelative += string(os.PathSeparator) } // 获取 srcFull 下的文件或子目录列表 fis, er := ioutil.ReadDir(srcFull) if er != nil { return er } // 开始遍历 for _, fi := range fis { if fi.IsDir() { logger.Debug("下层目录") tarDir(srcBase, srcRelative+fi.Name(), tw, fi) } else { logger.Debug("是文件") tarFile(srcBase, srcRelative+fi.Name(), tw, fi) } } return nil } // 因为要在 defer 中关闭文件,所以要单独创建一个函数 func tarFile(srcBase, srcRelative string, tw *tar.Writer, fi os.FileInfo) (err error) { // 获取完整路径 srcFull := srcBase + srcRelative logger.Debug("文件的全路径", srcFull) // 写入文件信息 hdr, er := tar.FileInfoHeader(fi, "") if er != nil { return er } logger.Debug(srcRelative) hdr.Name = srcRelative logger.Debug("文件hdr.name", hdr.Name) if er = tw.WriteHeader(hdr); er != nil { return er } // 打开要打包的文件,准备读取 fr, er := os.Open(srcFull) if er != nil { return er } defer fr.Close() // 将文件数据写入 tw 中 if _, er = io.Copy(tw, fr); er != nil { return er } return nil } //解包 可以解压,压缩文件里有文件夹的,也可以解压压缩文件里全是文件的 //srcGz待解包文件 //dstDir 解包之后存放的目录 func UnTarGz(srcGz string, dstDir string) error { dstDir = path.Clean(dstDir) + string(os.PathSeparator) //打开压缩文件 fr, err := os.Open(srcGz) if err != nil { return err } defer fr.Close() gw, err := gzip.NewReader(fr) defer gw.Close() // 创建 tar.Reader 对象,准备执行解包操作 // 可以用 tr.Next() 来遍历包中的文件 tr := tar.NewReader(gw) // 遍历包中的文件 for hdr, er := tr.Next(); er != io.EOF; hdr, er = tr.Next() { if er != nil { logger.Debug("--------- er:", er) return er } // 获取文件信息 fi := hdr.FileInfo() // 获取绝对路径 dstFullPath := dstDir + hdr.Name logger.Debug("--- dstFullPath:", dstFullPath) if hdr.Typeflag == tar.TypeDir { logger.Debug("--- type is floder") // 创建目录 os.MkdirAll(dstFullPath, fi.Mode().Perm()) // 设置目录权限 os.Chmod(dstFullPath, fi.Mode().Perm()) } else { logger.Debug(dstFullPath, "--- type is file") // 创建文件所在的目录 strtemp := dstFullPath[:strings.LastIndex(dstFullPath, "\\")+1] //os.MkdirAll(path.Dir(dstFullPath), os.ModePerm) os.MkdirAll(strtemp, os.ModePerm) // 将 tr 中的数据写入文件中 if er := unTarFile(dstFullPath, tr); er != nil { logger.Debug("unTarFile:",er) return er } // 设置文件权限 os.Chmod(dstFullPath, fi.Mode().Perm()) } } return nil } func UnTarGzByCmd(srcFile string, dstPath string) (string,error) { cmd := exec.Command("tar","-zxvf", srcFile, "-C", dstPath) outPut, err := cmd.Output() return string(outPut), err } // 因为要在 defer 中关闭文件,所以要单独创建一个函数 func unTarFile(dstFile string, tr *tar.Reader) error { // 创建空文件,准备写入解包后的数据 fw, er := os.Create(dstFile) if er != nil { return er } defer fw.Close() // 写入解包后的数据 _, er = io.Copy(fw, tr) if er != nil { return er } return nil } // 判断文件是否存在 func FileExists(filename string) bool { fi, err := os.Stat(filename) return (err == nil || os.IsExist(err)) && !fi.IsDir() } // 判断目录是否存在 func DirExists(dirname string) bool { fi, err := os.Stat(dirname) return (err == nil || os.IsExist(err)) && fi.IsDir() } func CopyDirByCmd(src ,dest string) (string,error) { cmd := exec.Command("cp","-r", src, dest) outPut, err := cmd.Output() return string(outPut), err } //文件夹复制 func CopyDir(src string, dest string) { src_original := src err := filepath.Walk(src, func(src string, f os.FileInfo, err error) error { if f == nil { return err } if f.IsDir() { CopyDir(f.Name(), dest+"/"+f.Name()) } else { fmt.Println(src) fmt.Println(src_original) fmt.Println(dest) dest_new := strings.Replace(src, src_original, dest, -1) fmt.Println(dest_new) fmt.Println("CopyFile:" + src + " to " + dest_new) CopyFile(src, dest_new) } return nil }) if err != nil { fmt.Printf("filepath.Walk() returned %v\n", err) } } //大文件复制 func CopyFile(srcFileName string, dstFileName string) { //打开源文件 srcFile, err := os.Open(srcFileName) if err != nil { log.Fatalf("源文件读取失败,原因是:%v\n", err) } defer func() { err = srcFile.Close() if err != nil { log.Fatalf("源文件关闭失败,原因是:%v\n", err) } }() //创建目标文件,稍后会向这个目标文件写入拷贝内容 distFile, err := os.Create(dstFileName) if err != nil { log.Fatalf("目标文件创建失败,原因是:%v\n", err) } defer func() { err = distFile.Close() if err != nil { log.Fatalf("目标文件关闭失败,原因是:%v\n", err) } }() //定义指定长度的字节切片,每次最多读取指定长度 var tmp = make([]byte, 1024*4) //循环读取并写入 for { n, err := srcFile.Read(tmp) n, _ = distFile.Write(tmp[:n]) if err != nil { if err == io.EOF {//读到了文件末尾,并且写入完毕,任务完成返回(关闭文件的操作由defer来完成) return } else { log.Fatalf("拷贝过程中发生错误,错误原因为:%v\n", err) } } } }