go语言的path/filepath包提供了很多兼容各个操作系统的文件路径实用操作方法,今天只来看看Walk方法:
Walk(root stirng, walkFn WalkFunc) error
该方法主要用于递归遍历目录:
walk方法会遍历root下的所有文件(包含root)并对每一个目录和文件都调用walkFunc方法。在访问文件和目录时发生的错误都会通过error参数传递给WalkFunc方法。文件是按照词法顺序进行遍历的,这个通常让输出更漂亮,但是也会导致处理非常大的目录时效率会降低。另外,Walk函数不会遍历符号链接。
方法名 | 定义 |
---|---|
WalkFunc | type WalkFunc func(path string, info os.FileInfo, err error) error |
Walk | func Walk(root string, walkFn WalkFunc) error |
type WalkFunc func(path string, info os.FileInfo, err error) 函数
根据文件信息path和info进行自定义操作:
WalkFunc是一个方法类型,Walk函数在遍历文件或者目录时调用。调用时将参数传递给path,将Walk函数中的root作为前缀。将root + 文件名或者目录名作为path传递给WalkFunc函数。例如在"dir"目录下遍历到"a"文件,则path="dir/a";Info是path所指向文件的文件信息。如果在遍历过程中出现了问题,传入参数err会描述这个问题。WalkFunc函数可以处理这个问题,Walk将不会再深入该目录。如果函数会返回一个错误,Walk函数会终止执行;只有一个例外,我们也通常用这个来跳过某些目录。当WalkFunc的返回值是filepaht.SkipDir时,Walk将会跳过这个目录,照常执行下一个文件。
Walk(root string, walkFn WalkFunc) 函数
//这里的参数root可以是文件名也可以是目录名;walkFn是自定义的函数func Walk(root string, walkFn WalkFunc) error { //获取root的描述信息 info, err := os.Lstat(root) if err != nil { //如果获取描述信息发生错误,返回err由定义的walkFn函数处理 err = walkFn(root, nil, err) } else { //调用walk(root, info, walkFn)函数进行递归遍历root err = walk(root, info, walkFn) } if err == SkipDir { return nil } return err }
func walk(path string, info os.FileInfo, walkFn WalkFunc) error 函数进行递归遍历
func walk(path string, info os.FileInfo, walkFn WalkFunc) error { //调用定义的walkFn自定义函数处理 err := walkFn(path, info, nil) if err != nil { //返回错误,且该目录可以跳过 if info.IsDir() && err == SkipDir { return nil } return err } //如果是文件,则遍历下一个 if !info.IsDir() { return nil } //读取该path下的所有目录和文件 names, err := readDirNames(path) if err != nil { //发生错误,调用自定义函数处理 return walkFn(path, info, err) } //遍历文件和目录列表 for _, name := range names { //路径path/name filename := Join(path, name) //获取该文件或者目录信息 fileInfo, err := lstat(filename) if err != nil { //发生错误,调用自定义函数处理 if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir { return err } } else { //这里递归调用,获取root下各级文件和目录信息,在自定义函数walkFn里做处理 err = walk(filename, fileInfo, walkFn) if err != nil { //遍历文件发生错误或者目录发生错误且不能跳过,则返回err if !fileInfo.IsDir() || err != SkipDir { return err } } } } return nil}
实例演练:
将一个目录或者文件压缩为zip包:
func main() { //oldFileName可以是文件或者目录 oldFileName := "root.log" currentTime := time.Now() //获取s mSecond := fmt.Sprintf("%03d", currentTime.Nanosecond() / 1e6) //zip文件名 zipFileName := strings.Split(oldFileName, ".")[0] + "_" + currentTime.Format("20060102150405") + mSecond + ".zip" //压缩文件 zipFile(oldFileName, zipFileName) }func zipFile(source, target string) error{ //创建目标zip文件 zipFile , err := os.Create(target) if err != nil { fmt.Println(err) return err } //关闭文件 defer zipFile.Close() //创建一个写zip的writer archive := zip.NewWriter(zipFile) defer archive.Close() return filepath.Walk(source, func(path string, info os.FileInfo, err error) error { if err != nil { return err } //将文件或者目录信息转换为zip格式的文件信息 header, err := zip.FileInfoHeader(info) if err != nil{ return err } if !info.IsDir() { // 确定采用的压缩算法(这个是内建注册的deflate) header.Method = zip.Deflate } // header.SetModTime(time.Unix(info.ModTime().Unix(), 0)) //文件或者目录名 header.Name = path //创建在zip内的文件或者目录 writer, err := archive.CreateHeader(header) if err != nil{ return err } //如果是目录,只需创建无需其他操作 if info.IsDir() { return nil } //打开需要压缩的文件 file, err := os.Open(path) if err != nil{ return err } defer file.Close() //将待压缩文件拷贝给zip内文件 _, err = io.Copy(writer, file) return err }) }
这种压缩文件的方式避免了zip包在linux上解压以后文件的修改时间为1979年12月31日的问题
linux上解压后的时间和原文件时间一样
package mainimport ( "time" "fmt" "strings" "os" "archive/zip" "io/ioutil" "path/filepath" "io")func main() { //oldFileName可以是文件或者目录 oldFileName := "root.log" compressZip(oldFileName) }func compressZip(oldFileName string) string{ fd, err := ioutil.ReadFile(oldFileName) if err != nil { fmt.Println("ReadFile ", oldFileName, "is error ", err) return "" } currentTime := time.Now() mSecond := fmt.Sprintf("%03d", currentTime.Nanosecond() / 1e6) zipFileName := strings.Split(oldFileName, ".")[0] + "_" + currentTime.Format("20060102150405") + mSecond + ".zip" fw, err := os.OpenFile(zipFileName, os.O_RDWR | os.O_CREATE | os.O_TRUNC, 400) if err != nil { fmt.Println("OpenFile ", zipFileName, "is error ", err) return "" } defer fw.Close() w := zip.NewWriter(fw) defer w.Close() f, err := w.Create(filepath.Base(oldFileName)) if err != nil { fmt.Println("Create ", oldFileName, "is error ", err) return "" } _, err = f.Write(fd) if err != nil { fmt.Println("Write newFileName is error ", err) return "" } return zipFileName }
以上这种方式压缩的zip包在linux上解压后时间为1979年12月31日。不利于生产环境问题定位。
解压后时间不对
作者:我的小碗汤
链接:https://www.jianshu.com/p/f590bb8dffe5