如何在Golang中压缩包含子目录或文件的目录?

ano*_*ose 6 zip go

我知道它将与zip包有关我只是不知道我将如何实现这样的事情.

dan*_*neu 12

这是使用 Go 的内置递归文件遍历器的解决方案,因为到目前为止最佳答案已经实现了它们自己的文件遍历器:


另外,我今天在生成 zip 文件时发现的一些发现可能会让其他人头疼:

  • 的参数w.Create(zippath)不应以“/”开头,而应相对于 zip 根目录(也就是解压缩存档时创建的文件夹)。所以顶级“manifest.xml”文件应该是w.Create("manifest.xml"). 嵌套文件应该是w.Create("a/b/c.css). 如果您正在生成不良/令人惊讶的档案,请先检查以确保您没有违反此规则。我的代码不会尝试强制执行此操作。
  • 一些规范(如 epub)希望文件按特定顺序排列,但 Gofilepath.Walk会按词法顺序爬行。(就此而言,我发现到目前为止,从 Calibre 到 macOS 上的 Books.app,所有 epub 解析器都比较宽松)。如果您需要特定的顺序,那么@LeTigre 的解决方案ReadDir将让您在每个下降级别对文件进行排序。

package main

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

// Zips "./input" into "./output.zip"
func main() {
    file, err := os.Create("output.zip")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    w := zip.NewWriter(file)
    defer w.Close()

    walker := func(path string, info os.FileInfo, err error) error {
        fmt.Printf("Crawling: %#v\n", path)
        if err != nil {
            return err
        }
        if info.IsDir() {
            return nil
        }
        file, err := os.Open(path)
        if err != nil {
            return err
        }
        defer file.Close()

        // Ensure that `path` is not absolute; it should not start with "/".
        // This snippet happens to work because I don't use 
        // absolute paths, but ensure your real-world code 
        // transforms path into a zip-root relative path.
        f, err := w.Create(path)
        if err != nil {
            return err
        }

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

        return nil
    }
    err = filepath.Walk("input", walker)
    if err != nil {
        panic(err)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 无需全部读取然后写入。只是做 io.Copy(dst, src) 所以目前 io.Copy(f, file) 另外你关闭 w 两次,这本身不是一个错误,但 zip.Close 不会关闭底层资源和在打开的目标文件程序的开头没有被关闭。所以目前可能会出现问题。而且该程序不完整(没有包规范,没有导入规范等) (3认同)
  • (郑重声明,我的回答确实包含了 @mh-cbon 的所有建议,iirc。谢谢。) (2认同)

小智 5

要手动执行此操作,您可以修改上面链接的代码:

ExampleZipWriter

举一个简单的例子,它有很多缺陷,但可能很容易理解:

func ZipWriter() {
    baseFolder := "/Users/tom/Desktop/testing/"

    // Get a Buffer to Write To
    outFile, err := os.Create(`/Users/tom/Desktop/zip.zip`)
    if err != nil {
        fmt.Println(err)
    }
    defer outFile.Close()

    // Create a new zip archive.
    w := zip.NewWriter(outFile)

    // Add some files to the archive.
    addFiles(w, baseFolder, "")

    if err != nil {
        fmt.Println(err)
    }

    // Make sure to check the error on Close.
    err = w.Close()
    if err != nil {
        fmt.Println(err)
    }
}
Run Code Online (Sandbox Code Playgroud)

我们使用它来递归地遍历文件以生成文件夹:

func addFiles(w *zip.Writer, basePath, baseInZip string) {
    // Open the Directory
    files, err := ioutil.ReadDir(basePath)
    if err != nil {
        fmt.Println(err)
    }

    for _, file := range files {
        fmt.Println(basePath + file.Name())
        if !file.IsDir() {
            dat, err := ioutil.ReadFile(basePath + file.Name())
            if err != nil {
                fmt.Println(err)
            }

            // Add some files to the archive.
            f, err := w.Create(baseInZip + file.Name())
            if err != nil {
                fmt.Println(err)
            }
            _, err = f.Write(dat)
            if err != nil {
                fmt.Println(err)
            }
        } else if file.IsDir() {

            // Recurse
            newBase := basePath + file.Name() + "/"
            fmt.Println("Recursing and Adding SubDir: " + file.Name())
            fmt.Println("Recursing and Adding SubDir: " + newBase)

            addFiles(w, newBase, baseInZip  + file.Name() + "/")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)