如何在Go中将文件压缩为不带目录文件夹的.zip

There're examples about compressing a file to .zip in Go. However, the file they generate include the directory folder. When I decompress the .zip file, there will be a new folder.

So, how can I compress a file to .zip without getting the directory folder included?

An example:

https://golangcode.com/create-zip-files-in-go/

package main

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

func main() {

    // List of Files to Zip
    files := []string{"example.csv", "data.csv"}
    output := "done.zip"

    if err := ZipFiles(output, files); err != nil {
        panic(err)
    }
    fmt.Println("Zipped File:", output)
}

// ZipFiles compresses one or many files into a single zip archive file.
// Param 1: filename is the output zip file's name.
// Param 2: files is a list of files to add to the zip.
func ZipFiles(filename string, files []string) error {

    newZipFile, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer newZipFile.Close()

    zipWriter := zip.NewWriter(newZipFile)
    defer zipWriter.Close()

    // Add files to zip
    for _, file := range files {
        if err = AddFileToZip(zipWriter, file); err != nil {
            return err
        }
    }
    return nil
}

func AddFileToZip(zipWriter *zip.Writer, filename string) error {

    fileToZip, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer fileToZip.Close()

    // Get the file information
    info, err := fileToZip.Stat()
    if err != nil {
        return err
    }

    header, err := zip.FileInfoHeader(info)
    if err != nil {
        return err
    }

    // Using FileInfoHeader() above only uses the basename of the file. If we want
    // to preserve the folder structure we can overwrite this with the full path.
    header.Name = filename

    // Change to deflate to gain better compression
    // see http://golang.org/pkg/archive/zip/#pkg-constants
    header.Method = zip.Deflate

    writer, err := zipWriter.CreateHeader(header)
    if err != nil {
        return err
    }
    _, err = io.Copy(writer, fileToZip)
    return err
}

Just use a base name of the file in the zip header.

header.Name = filepath.Base(filename)
              ^^^^^^^^^^^^^^

Here is a version that does the same thing

package main

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

func createFlatZip(w io.Writer, files ...string) error {
    z := zip.NewWriter(w)
    for _, file := range files {
        src, err := os.Open(file)
        if err != nil {
            return err
        }
        info, err := src.Stat()
        if err != nil {
            return err
        }
        hdr, err := zip.FileInfoHeader(info)
        if err != nil {
            return err
        }
        hdr.Name = filepath.Base(file) // Write only the base name in the header
        dst, err := z.CreateHeader(hdr)
        if err != nil {
            return err
        }
        _, err = io.Copy(dst, src)
        if err != nil {
            return err
        }
        src.Close()
    }
    return z.Close()
}

func main() {
    if len(os.Args) < 3 {
        log.Fatalf("archive name and at least one file are required")
    }
    a, err := os.Create(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    defer a.Close()

    err = createFlatZip(a, os.Args[2:]...)
    if err != nil {
        log.Fatal(err)
    }
}

Results:

~/src/gozip
➜ go build

~/src/gozip
➜ mkdir test && echo 1 > test/1.txt # create a test file in a subfolder

~/src/gozip
➜ ./gozip 1.zip test/1.txt

~/src/gozip
➜ unzip -l 1.zip
Archive:  1.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        2  08-15-2019 01:29   1.txt
---------                     -------
        2                     1 file