Golang 实现tar.gz打包

后端存储 addops

可以解决常见的”archive/tar: write too long”问题。

官方对tar包func FileInfoHeader的描述是这样的

FileInfoHeader creates a partially-populated Header from fi. If fi describes a symlink, FileInfoHeader records link as the link target. If fi describes a directory, a slash is appended to the name. Because os.FileInfo’s Name method returns only the base name of the file it describes, it may be necessary to modify the Name field of the returned header to provide the full path name of the file.

事实上FileInfoHeader无法保证Header字段在写入WriteHeader前是完整的,通常Size字段只设置在常规文件中。当在遇到非常规文件时,虽然Hearder中的Size为0,却会尝试将一个Size非零的文件加入tar包。此时Go就会返回ErrWriteTooLong来阻止创建一个损坏的tar包.

具体代码如下:

func TarFilesDirs(path string, tarFilePath string) error {
    file, err := os.Create(tarFilePath)
    if err != nil {
        return err
    }

    defer file.Close()
    gz := gzip.NewWriter(file)
    defer gz.Close()

    tw := tar.NewWriter(gz)
    defer tw.Close()

    if err := tarit(path, tw); err != nil {
        return err
    }

    return nil
}

func tarit(source string, tw *tar.Writer) error {
    info, err := os.Stat(source)
    if err != nil {
        return nil
    }
    var baseDir string
    if info.IsDir() {
        baseDir = filepath.Base(source)
    }

    return filepath.Walk(source,
        func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return err
            }

            var link string
            if info.Mode()&os.ModeSymlink != 0 {
                if link, err = os.Readlink(path); err != nil {
                    return err
                }
            }
            header, err := tar.FileInfoHeader(info, link)
            if err != nil {
                return err
            }
            if baseDir != "" {
                header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
            }

            if !info.Mode().IsRegular() { //nothing more to do for non-regular
                return nil
            }
            if err := tw.WriteHeader(header); err != nil {
                return err
            }

            if info.IsDir() {
                return nil
            }

            file, err := os.Open(path)
            if err != nil {
                return err
            }

            defer file.Close()

            buf := make([]byte, 16)
            if _, err = io.CopyBuffer(tw, file, buf); err != nil {
                return err
            }

            return nil
        })
}
稿源:addops (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 后端存储 » Golang 实现tar.gz打包

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录