3. archive 包

在本节中,我们将详细介绍 Go 语言标准库中的 archive 包,包括 archive/tararchive/zip。我们将展示如何使用这些包进行归档和解压操作,并提供实际的代码示例。

3.1 archive/tar

archive/tar 包提供了对 tar 格式文件的读写支持。tar 格式常用于将多个文件和目录打包成一个文件。

3.1.1 基本使用

要使用 archive/tar 包,需要导入该包并创建 tar.Writertar.Reader 来进行归档和解压操作。

import (
    "archive/tar"
    "os"
    "io"
)
3.1.2 创建和解压 tar 文件

创建 tar 文件:

func createTar(outputFile string, files []string) error {
    outFile, err := os.Create(outputFile)
    if err != nil {
        return err
    }
    defer outFile.Close()

    tarWriter := tar.NewWriter(outFile)
    defer tarWriter.Close()

    for _, file := range files {
        err := addFileToTar(tarWriter, file)
        if err != nil {
            return err
        }
    }

    return nil
}

func addFileToTar(tw *tar.Writer, filePath string) error {
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer file.Close()

    stat, err := file.Stat()
    if err != nil {
        return err
    }

    header := &tar.Header{
        Name: filePath,
        Size: stat.Size(),
        Mode: int64(stat.Mode()),
        ModTime: stat.ModTime(),
    }
    err = tw.WriteHeader(header)
    if err != nil {
        return err
    }

    _, err = io.Copy(tw, file)
    return err
}

解压 tar 文件:

func extractTar(inputFile, outputDir string) error {
    inFile, err := os.Open(inputFile)
    if err != nil {
        return err
    }
    defer inFile.Close()

    tarReader := tar.NewReader(inFile)

    for {
        header, err := tarReader.Next()
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }

        outputFile := outputDir + "/" + header.Name
        err = os.MkdirAll(outputDir+"/"+header.Name, os.FileMode(header.Mode))
        if err != nil {
            return err
        }

        if header.Typeflag == tar.TypeDir {
            continue
        }

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

        _, err = io.Copy(outFile, tarReader)
        if err != nil {
            return err
        }
    }

    return nil
}
3.1.3 实践案例

假设我们有两个文本文件 file1.txtfile2.txt,内容如下:

file1.txt:
Hello, this is file1.

file2.txt:
Hello, this is file2.

我们可以使用上面的函数将它们打包成 archive.tar,然后再解压到 output 目录。

func main() {
    files := []string{"file1.txt", "file2.txt"}
    tarFile := "archive.tar"
    outputDir := "output"

    // 创建 tar 文件
    err := createTar(tarFile, files)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Tar file created successfully")

    // 解压 tar 文件
    err = extractTar(tarFile, outputDir)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Tar file extracted successfully")
}

运行此代码后,我们会看到控制台输出:

Tar file created successfully
Tar file extracted successfully

并且在当前目录下生成 archive.tar 文件和 output 目录,其中包含 file1.txtfile2.txt 文件。

3.2 archive/zip

archive/zip 包提供了对 zip 格式文件的读写支持。zip 格式是一种常见的压缩和归档格式。

3.2.1 基本使用

要使用 archive/zip 包,需要导入该包并创建 zip.Writerzip.Reader 来进行归档和解压操作。

import (
    "archive/zip"
    "os"
    "io"
    "path/filepath"
)
3.2.2 创建和解压 zip 文件

创建 zip 文件:

func createZip(outputFile string, files []string) error {
    outFile, err := os.Create(outputFile)
    if err != nil {
        return err
    }
    defer outFile.Close()

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

    for _, file := range files {
        err := addFileToZip(zipWriter, file)
        if err != nil {
            return err
        }
    }

    return nil
}

func addFileToZip(zw *zip.Writer, filePath string) error {
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer file.Close()

    stat, err := file.Stat()
    if err != nil {
        return err
    }

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

    header.Name = filepath.Base(filePath)
    header.Method = zip.Deflate

    writer, err := zw.CreateHeader(header)
    if err != nil {
        return err
    }

    _, err = io.Copy(writer, file)
    return err
}

解压 zip 文件:

func extractZip(inputFile, outputDir string) error {
    zipReader, err := zip.OpenReader(inputFile)
    if err != nil {
        return err
    }
    defer zipReader.Close()

    for _, file := range zipReader.File {
        outputFile := filepath.Join(outputDir, file.Name)

        if file.FileInfo().IsDir() {
            os.MkdirAll(outputFile, os.ModePerm)
            continue
        }

        if err := os.MkdirAll(filepath.Dir(outputFile), os.ModePerm); err != nil {
            return err
        }

        outFile, err := os.OpenFile(outputFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
        if err != nil {
            return err
        }
        defer outFile.Close()

        rc, err := file.Open()
        if err != nil {
            return err
        }
        defer rc.Close()

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

    return nil
}
3.2.3 实践案例

我们可以使用上面的函数将 file1.txtfile2.txt 打包成 archive.zip,然后再解压到 output 目录。

func main() {
    files := []string{"file1.txt", "file2.txt"}
    zipFile := "archive.zip"
    outputDir := "output"

    // 创建 zip 文件
    err := createZip(zipFile, files)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Zip file created successfully")

    // 解压 zip 文件
    err = extractZip(zipFile, outputDir)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Zip file extracted successfully")
}

运行此代码后,我们会看到控制台输出:

Zip file created successfully
Zip file extracted successfully

并且在当前目录下生成 archive.zip 文件和 output 目录,其中包含 file1.txtfile2.txt 文件。