GOLANG:步行目录树和处理文件— err ='没有这样的文件或目录

I'm writing a routine to walk a directory tree and create a digital signature (salted-hash) for each file I find. When testing it I get this weird behavior - if I give the program a root path "above" the directory, the program can walk the tree and print out the file names, but if I try and open the file to read it's bytes, I get the error message "no such file or directory" on the file that the routines found - not sure what gives here. How can the Walk() routine "see" the file, but ioutil.ReadFile() not be able to find it?

Sample Code:

// start with path higher up the tree, say $HOME
func doHashWalk(dirPath string) {
    err := filepath.Walk(dirPath, walkFn)
    // Check err here
}

func walkFn(path string, fi os.FileInfo, err error) (e error) {

    if !fi.IsDir() {
        // if the first character is a ".", then skip it as it's a hidden file

        if strings.HasPrefix(fi.Name(), ".") {
            return nil
        }

        // read in the file bytes -> get the absolute path
        fullPath, err := filepath.Abs(fi.Name())
        if err != nil {
            log.Printf("Abs Err: %s", err)
        }

        // THIS ALWAYS FAILED WITH ERROR
        bytes, err := ioutil.ReadFile(fullPath) // <-- (fi.Name() also doesn't work)
        if err != nil {
            log.Printf("Err: %s, Bytes: %d", err, len(bytes))     
        }

        // create the salted hash
        ...
    }
    return nil
}

Try logging the values of path vs. fullPath inside of walkFn.

Using filepath.Abs() inside of walkFn does not give the result you want: it's resolving a filename relative to the current working directory, instead of the original dirPath.

One option is to resolve the target directory to an absolute path up-front in doHashWalk:

func doHashWalk(dirPath string) {
    fullPath, err := filepath.Abs(dirPath)

    if err != nil {
        log.Println("path error:", err)

        return
    }

    err = filepath.Walk(fullPath, walkFn)

    // check err here
}

With that change, the walkFn callback will always receive a fully-qualified path argument; no need to call filepath.Abs() again:

func walkFn(path string, fi os.FileInfo, err error) (e error) {
    // ...

    bytes, err := ioutil.ReadFile(path)

    // ...
}

If it's important for your application to see the path of each file relative to the original dirPath root, you can sneak that path into the walkFn callback via a closure:

func doHashWalk(dirPath string) error {

    fullPath, err := filepath.Abs(dirPath)

    if err != nil {
        return err
    }

    callback := func(path string, fi os.FileInfo, err error) error {
        return hashFile(fullPath, path, fi, err)
    }

    return filepath.Walk(fullPath, callback)
}

func hashFile(root string, path string, fi os.FileInfo, err error) error {
    if fi.IsDir() {
        return nil
    }

    rel, err := filepath.Rel(root, path)

    if err != nil {
        return err
    }

    log.Println("hash rel:", rel, "abs:", path)

    return nil
}

The problems is that you're not using the filepath.Walk exactly the way it's intended. You allready get the path to the file from the path parameter.

The docs might not exactly be abundantly clear but they do say this for filepath.Walk:

Walk walks the file tree rooted at root, calling walkFn for each file or directory in the tree, including root.

So you could shorten walkFn to something like this:

func walkFn(path string, fi os.FileInfo, err error) (e error) {
    if !fi.IsDir() {
        bytes, err := ioutil.ReadFile(path) // path is the path to the file.
        if err != nil {
            fmt.Println("Fail")
        }
    }
    return nil
}

Walk will take care of not walking up the file-tree via .. there's no need for you to check for that. Considering the absolute path part I don't really think you need that either, but I make no promises ;)