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

lp0sw83n  于 2023-02-27  发布在  Go
关注(0)|答案(4)|浏览(193)

我知道这将与zip package有关,我只是不知道我将如何实现这样的事情。

vaqhlq81

vaqhlq811#

下面的解决方案使用了Go语言内置的递归文件遍历器,因为到目前为止最好的解决方案已经实现了自己的文件遍历器:
除此之外,我今天在生成zip文件时发现的一些发现可能会让其他人省去一个头疼的问题:

  • w.Create(zippath)的参数不应以“/"开头,并且应相对于zip根目录(也就是解压归档文件时创建的文件夹)。因此,顶层“manifest.xml”文件应为w.Create("manifest.xml")。嵌套文件应为w.Create("a/b/c.css)。如果生成的归档文件不好/令人惊讶,请先检查以确保您没有违反此规则。我的代码不会强制执行此规则。
  • 有些规范(比如epub)要求文件按照一定的顺序,但Go语言的filepath.Walk会按照词法顺序爬行(在这方面,我发现所有的epub解析器在macOS上都比较宽松,从Calibre到Books.app都是如此)。如果你需要一个特定的顺序,那么@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)
    }
}
yr9zkbsy

yr9zkbsy2#

要手动执行此操作,您可以修改上面链接的代码:
示例ZipWriter
给予一个简单的例子,它有很多缺陷,但可能很容易理解:

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)
    }
}

我们也用它来递归地迭代文件以生成文件夹:

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() + "/")
        }
    }
}
xdyibdwo

xdyibdwo3#

@danneu的答案是好的,但是如果目录包含空的子目录,代码就不能工作,它会忽略它们。
因此,我们不应只返回nil,而应创建一个目录:

if info.IsDir() {
    // add a trailing slash for creating dir
    path = fmt.Sprintf("%s%c", path, os.PathSeparator)
    _, err = w.Create(path)
    return err
}
55ooxyrt

55ooxyrt4#

一个ez的方式压缩目录,你可以运行命令的服务器:

out, err := exec.Command("zip", "-r", "-D", "ideaz.zip", ".idea/*").Output()

https://play.golang.org/p/Mn-HmUjm5q2

相关问题