前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go语言path/filepath包之Walk源码解析

go语言path/filepath包之Walk源码解析

作者头像
我的小碗汤
发布2018-08-22 11:19:27
1.2K0
发布2018-08-22 11:19:27
举报
文章被收录于专栏:我的小碗汤我的小碗汤

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) 函数
代码语言:javascript
复制
//这里的参数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 函数进行递归遍历
代码语言:javascript
复制
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包:

代码语言:javascript
复制
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

    })
}

运行效果如下图:

linux上解压后的时间和原文件时间一样

这种压缩文件的方式避免了zip包在linux上解压以后文件的修改时间为1979年12月31日的问题。



代码语言:javascript
复制
package main

import (
    "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日,如下图。不利于生产环境问题定位。

解压后时间不对

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 进击云原生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实例演练:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档