使用golang解压缩文件的简便方法

Aks*_*iri 26 zip unzip go

有没有一种简单的方法用golang解压缩文件?

现在我的代码是:

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer r.Close()

    for _, f := range r.File {
        rc, err := f.Open()
        if err != nil {
            return err
        }
        defer rc.Close()

        path := filepath.Join(dest, f.Name)
        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            f, err := os.OpenFile(
                path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer f.Close()

            _, err = io.Copy(f, rc)
            if err != nil {
                return err
            }
        }
    }

    return nil
}
Run Code Online (Sandbox Code Playgroud)

小智 41

稍微修改OP的解决方案以创建包含目录(dest如果它不存在),并将文件提取/写入包装在一个闭包中,以消除@Nick Craig-Wood的评论中的堆栈defer .Close()调用:

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer func() {
        if err := r.Close(); err != nil {
            panic(err)
        }
    }()

    os.MkdirAll(dest, 0755)

    // Closure to address file descriptors issue with all the deferred .Close() methods
    extractAndWriteFile := func(f *zip.File) error {
        rc, err := f.Open()
        if err != nil {
            return err
        }
        defer func() {
            if err := rc.Close(); err != nil {
                panic(err)
            }
        }()

        path := filepath.Join(dest, f.Name)

        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            os.MkdirAll(filepath.Dir(path), f.Mode())
            f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer func() {
                if err := f.Close(); err != nil {
                    panic(err)
                }
            }()

            _, err = io.Copy(f, rc)
            if err != nil {
                return err
            }
        }
        return nil
    }

    for _, f := range r.File {
        err := extractAndWriteFile(f)
        if err != nil {
            return err
        }
    }

    return nil
}
Run Code Online (Sandbox Code Playgroud)

注意:更新以包括Close()错误处理(如果我们正在寻找最佳实践,也可以遵循所有这些).

  • 最佳答案.我非常感谢关闭.解压缩包含太多文件的档案时,我遇到了问题.这解决了这个问题. (2认同)
  • 此示例不适用于具有嵌套目录的zip文件,因为从未创建输出目录.代码假定目录有一个单独的条目,例如`some/dir /`然后可以创建,之后可以提取`some/dir/file.txt`.但文件只是单独列出其完整路径(没有单独的目录条目).这导致它失败,因为在尝试在其中创建文件之前未创建目录.我编辑了答案来解决这个问题. (2认同)
  • 如果zip文件不可信,可能会导致目录遍历漏洞。请参阅https://gist.github.com/virusdefender/f8c127ab420942dd99acbf269aca9cc7 (2认同)

swt*_*rgn 6

我正在使用archive/zip包读取.zip文件并复制到本地磁盘.下面是根据自己的需要解压缩.zip文件的源代码.

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

func unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer r.Close()

    for _, f := range r.File {
        rc, err := f.Open()
        if err != nil {
            return err
        }
        defer rc.Close()

        fpath := filepath.Join(dest, f.Name)
        if f.FileInfo().IsDir() {
            os.MkdirAll(fpath, f.Mode())
        } else {
            var fdir string
            if lastIndex := strings.LastIndex(fpath,string(os.PathSeparator)); lastIndex > -1 {
                fdir = fpath[:lastIndex]
            }

            err = os.MkdirAll(fdir, f.Mode())
            if err != nil {
                log.Fatal(err)
                return err
            }
            f, err := os.OpenFile(
                fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer f.Close()

            _, err = io.Copy(f, rc)
            if err != nil {
                return err
            }
        }
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)