Golang中os.File如何实现FileInfo()方法

Golang中os.File如何实现FileInfo()方法 你好,

我在搜索如何使用Go解压文件并尝试理解代码时,在这个步骤卡住了:f.FileInfo()

os.FileFileInfo() 函数实现在哪里?

我在GitHub上的Go代码库中搜索但没有找到,有人能帮忙指出具体位置吗?

以下是我作为参考的代码(来自 https://golangcode.com/unzip-files-in-go/

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "log"
    "os"
    "path/filepath"
    "strings"
)

func main() {

    files, err := Unzip("done.zip", "output-folder")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Unzipped:\n" + strings.Join(files, "\n"))
}

// Unzip will decompress a zip archive, moving all files and folders
// within the zip file (parameter 1) to an output directory (parameter 2).
func Unzip(src string, dest string) ([]string, error) {

    var filenames []string

    r, err := zip.OpenReader(src)
    if err != nil {
        return filenames, err
    }
    defer r.Close()

    for _, f := range r.File {

        // Store filename/path for returning and using later on
        fpath := filepath.Join(dest, f.Name)

        // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
        if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
            return filenames, fmt.Errorf("%s: illegal file path", fpath)
        }

        filenames = append(filenames, fpath)

        if f.FileInfo().IsDir() {
            // Make Folder
            os.MkdirAll(fpath, os.ModePerm)
            continue
        }

        // Make File
        if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
            return filenames, err
        }

        outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
        if err != nil {
            return filenames, err
        }

        rc, err := f.Open()
        if err != nil {
            return filenames, err
        }

        _, err = io.Copy(outFile, rc)

        // Close the file without defer to close before next iteration of loop
        outFile.Close()
        rc.Close()

        if err != nil {
            return filenames, err
        }
    }
    return filenames, nil
}

更多关于Golang中os.File如何实现FileInfo()方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

这类似于

zip 
  FileHeader
    FileInfoHeader  
	   FileHeader  
	      FileInfo

更多关于Golang中os.File如何实现FileInfo()方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


FileInfo 是一个接口

type FileInfo interface {
    Name() string       // 文件的基本名称
    Size() int64        // 常规文件的字节长度;其他文件类型取决于系统
    Mode() FileMode     // 文件模式位
    ModTime() time.Time // 修改时间
    IsDir() bool        // Mode().IsDir() 的简写形式
    Sys() interface{}   // 底层数据源(可能返回 nil)
}

哦抱歉,我误解了文档,我以为结构体Reader(由archive/zip中的ReadCloser实现)使用的是os.File,但实际上它使用的是archive/zip中的File(正如建议的那样,它实现了FileHeader

type Reader struct {
        r             io.ReaderAt
        File          []*File
        Comment       string
        decompressors map[uint16]Decompressor
}

type ReadCloser struct {
        f *os.File
        Reader
}

type File struct {
        FileHeader
        zip          *Reader
        zipr         io.ReaderAt
        zipsize      int64
        headerOffset int64
}

谢谢!

是的,但文件并未实现此接口(https://github.com/golang/go/blob/release-branch.go1.13/src/os/types.go#L16)

// File represents an open file descriptor.
type File struct {
	*file // os specific
}

在以下链接中也没有实现(https://github.com/golang/go/blob/release-branch.go1.13/src/os/file_unix.go#L45:)

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
	pfd         poll.FD
	name        string
	dirinfo     *dirInfo // nil unless directory being read
	nonblock    bool     // whether we set nonblocking mode
	stdoutOrErr bool     // whether this is stdout or stderr
	appendMode  bool     // whether file is opened for appending
}

如果未实现接口的函数,我该如何将接口作为函数调用?

在您提供的代码中,f.FileInfo() 方法调用的是 zip.File 类型的 FileInfo() 方法,而不是 os.File 的方法。让我详细解释一下:

zip.File 的 FileInfo() 方法

zip.File 类型确实有一个 FileInfo() 方法,它返回一个 os.FileInfo 接口。这个方法在 Go 标准库的 archive/zip 包中实现。

源码位置

在 Go 源码中,zip.FileFileInfo() 方法实现位于: src/archive/zip/struct.go

实现原理

zip.File 结构体内部存储了文件的元数据信息,FileInfo() 方法会基于这些信息创建一个 fileInfo 结构体实例,该结构体实现了 os.FileInfo 接口的所有方法。

示例代码演示

package main

import (
    "archive/zip"
    "fmt"
    "log"
    "time"
)

func main() {
    // 打开 zip 文件
    r, err := zip.OpenReader("example.zip")
    if err != nil {
        log.Fatal(err)
    }
    defer r.Close()

    // 遍历 zip 中的文件
    for _, f := range r.File {
        // 调用 FileInfo() 方法获取文件信息
        fileInfo := f.FileInfo()
        
        fmt.Printf("文件名: %s\n", fileInfo.Name())
        fmt.Printf("文件大小: %d bytes\n", fileInfo.Size())
        fmt.Printf("是否是目录: %t\n", fileInfo.IsDir())
        fmt.Printf("修改时间: %s\n", fileInfo.ModTime().Format(time.RFC3339))
        fmt.Printf("权限模式: %s\n", fileInfo.Mode())
        fmt.Printf("系统信息: %v\n", fileInfo.Sys())
        fmt.Println("---")
    }
}

底层实现简化版

zip.FileFileInfo() 方法大致是这样实现的:

// 在 archive/zip 包中的实现
func (f *File) FileInfo() os.FileInfo {
    return &fileInfo{
        name:    f.Name,
        size:    int64(f.UncompressedSize64),
        mode:    f.Mode(),
        modTime: f.ModTime(),
    }
}

// fileInfo 结构体实现 os.FileInfo 接口
type fileInfo struct {
    name    string
    size    int64
    mode    os.FileMode
    modTime time.Time
}

func (fi *fileInfo) Name() string       { return fi.name }
func (fi *fileInfo) Size() int64        { return fi.size }
func (fi *fileInfo) Mode() os.FileMode  { return fi.mode }
func (fi *fileInfo) ModTime() time.Time { return fi.modTime }
func (fi *fileInfo) IsDir() bool        { return fi.mode.IsDir() }
func (fi *fileInfo) Sys() interface{}   { return nil }

在您的解压代码中,f.FileInfo().IsDir() 就是通过这种方式判断 zip 条目是否为目录的。

回到顶部