golang跨平台多格式压缩与归档处理插件库archives的使用

Golang跨平台多格式压缩与归档处理插件库archives的使用

介绍

mholt/archives 是一个跨平台、支持多种格式的Go库,用于处理归档和压缩格式,提供统一的API以及与io/fs兼容的虚拟文件系统。

功能特性

  • 流式API
  • 自动识别归档和压缩格式:
    • 通过文件名
    • 通过流头部探测
  • 统一遍历目录、归档和其他文件作为io/fs文件系统:
    • FileFS
    • DirFS
    • ArchiveFS
  • 使用DeepFS无缝进入归档文件
  • 压缩和解压文件
  • 创建和提取归档文件
  • 遍历归档文件
  • 从归档中提取特定文件
  • 向.tar和.zip归档中插入内容(追加)而无需重新创建整个归档
  • 支持多种归档和压缩格式
  • 读取受密码保护的7-Zip和RAR文件
  • 可扩展(通过注册添加更多格式)
  • 跨平台,静态二进制
  • 纯Go实现(无cgo)
  • 多线程Gzip
  • 可调压缩级别
  • 超快的Snappy实现(通过S2)

支持的压缩格式

  • brotli (.br)
  • bzip2 (.bz2)
  • flate (.zip)
  • gzip (.gz)
  • lz4 (.lz4)
  • lzip (.lz)
  • minlz (.mz)
  • snappy (.sz) 和 S2 (.s2)
  • xz (.xz)
  • zlib (.zz)
  • zstandard (.zst)

支持的归档格式

  • .zip
  • .tar (包括压缩变体如.tar.gz)
  • .rar (只读)
  • .7z (只读)

安装

$ go get github.com/mholt/archives

使用示例

创建归档

ctx := context.TODO()

// 将磁盘文件映射到归档中的路径
files, err := archives.FilesFromDisk(ctx, nil, map[string]string{
    "/path/on/disk/file1.txt": "file1.txt",
    "/path/on/disk/file2.txt": "subfolder/file2.txt",
    "/path/on/disk/file3.txt": "",              // 放在归档根目录作为file3.txt
    "/path/on/disk/file4.txt": "subfolder/",    // 放在子文件夹作为file4.txt
    "/path/on/disk/folder":    "Custom Folder", // 递归添加内容
})
if err != nil {
    return err
}

// 创建输出文件
out, err := os.Create("example.tar.gz")
if err != nil {
    return err
}
defer out.Close()

// 使用CompressedArchive类型来gzip压缩tar包
format := archives.CompressedArchive{
    Compression: archives.Gz{},
    Archival:    archives.Tar{},
}

// 创建归档
err = format.Archive(ctx, out, files)
if err != nil {
    return err
}

提取归档

// 用于读取输入流的类型
var format archives.Zip

err := format.Extract(ctx, input, func(ctx context.Context, f archives.FileInfo) error {
    // 在这里处理文件;或者如果你只想要特定文件或目录,
    // 只需返回直到遇到所需的f.NameInArchive值
    return nil
})
if err != nil {
    return err
}

识别格式

// 除非你的流是io.Seeker,否则使用返回的stream值以确保重新读取Identify()期间消耗的字节
format, stream, err := archives.Identify(ctx, "filename.tar.zst", stream)
if err != nil {
    return err
}

// 现在可以将format类型断言为你需要的类型

// 想要提取内容?
if ex, ok := format.(archives.Extractor); ok {
    // ... 继续提取
}

// 或者可能是压缩的,你想解压它?
if decomp, ok := format.(archives.Decompressor); ok {
    rc, err := decomp.OpenReader(unknownFile)
    if err != nil {
        return err
    }
    defer rc.Close()

    // 从rc读取获取解压后的数据
}

虚拟文件系统

// filename可以是:
// - 文件夹("/home/you/Desktop")
// - 归档("example.zip")
// - 压缩归档("example.tar.gz")
// - 常规文件("example.txt")
// - 压缩的常规文件("example.txt.gz")
// 和/或最后一个参数可以是上述任何内容的流
fsys, err := archives.FileSystem(ctx, filename, nil)
if err != nil {
    return err
}

压缩数据

// 包装底层writer w
compressor, err := archives.Zstd{}.OpenWriter(w)
if err != nil {
    return err
}
defer compressor.Close()

// 写入compressor的数据将被压缩

解压数据

// 包装底层reader r
decompressor, err := archives.Snappy{}.OpenReader(r)
if err != nil {
    return err
}
defer decompressor.Close()

// 从decompressor读取的数据将被解压

追加到tar和zip归档

tarball, err := os.OpenFile("example.tar", os.O_RDWR, 0644)
if err != nil {
    return err
}
defer tarball.Close()

// 准备一个文本文件放在归档根目录
files, err := archives.FilesFromDisk(nil, map[string]string{
    "/home/you/lastminute.txt": "",
})

err := archives.Tar{}.Insert(context.Background(), tarball, files)
if err != nil {
    return err
}

遍历归档内容

fsys := &archives.DeepFS{Root: "/some/dir"}

err := fs.WalkDir(fsys, ".", func(fpath string, d fs.DirEntry, err error) error {
    ...
})

注意事项

  • 对于.tar文件:由于tar文件的历史设计(针对顺序访问的磁带),它们不能高效地实现文件系统语义。文件系统本质上假设存在某种索引以支持随机访问,但tar文件需要从头开始读取才能访问末尾的内容。当归档被压缩时尤其慢。已实现优化来分摊ReadDir()调用,以便fs.WalkDir()只需扫描归档一次,但它们会使用更多内存。Open调用需要另一次扫描来查找文件。如果文件系统语义对你来说不重要,直接使用Tar.Extract()可能更高效。

  • 与http.FileServer一起使用时:由于http.FileServer的工作方式,不要直接将其与压缩文件一起使用;而是像下面这样包装它:

fileServer := http.FileServer(http.FS(archiveFS))
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    // 禁用范围请求
    writer.Header().Set("Accept-Ranges", "none")
    request.Header.Del("Range")
    
    // 禁用内容类型嗅探
    ctype := mime.TypeByExtension(filepath.Ext(request.URL.Path))
    writer.Header()["Content-Type"] = nil
    if ctype != "" {
        writer.Header().Set("Content-Type", ctype)
    }
    fileServer.ServeHTTP(writer, request)
})

更多关于golang跨平台多格式压缩与归档处理插件库archives的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang跨平台多格式压缩与归档处理插件库archives的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang跨平台多格式压缩与归档处理:archives库使用指南

archives是一个强大的Golang库,专门用于处理各种压缩和归档格式,支持跨平台操作。下面我将详细介绍如何使用这个库。

安装

首先安装archives库:

go get github.com/mholt/archiver/v3

基本使用

1. 压缩文件/目录

package main

import (
	"github.com/mholt/archiver/v3"
	"log"
)

func main() {
	// 创建ZIP压缩文件
	err := archiver.Archive([]string{"file1.txt", "folder"}, "archive.zip")
	if err != nil {
		log.Fatal(err)
	}
	
	// 创建TAR.GZ压缩文件
	err = archiver.Archive([]string{"file1.txt", "folder"}, "archive.tar.gz")
	if err != nil {
		log.Fatal(err)
	}
}

2. 解压文件

func extractExample() {
	// 解压ZIP文件
	err := archiver.Unarchive("archive.zip", "extracted")
	if err != nil {
		log.Fatal(err)
	}
	
	// 解压RAR文件
	err = archiver.Unarchive("archive.rar", "extracted")
	if err != nil {
		log.Fatal(err)
	}
}

支持的格式

archives库支持以下格式:

  • ZIP
  • TAR (包括.tar.gz, .tar.bz2, .tar.xz, .tar.lz4, .tar.sz)
  • GZIP
  • BZIP2
  • LZ4
  • SNAPPY
  • XZ
  • RAR (需要安装系统rar工具)

高级用法

1. 自定义压缩选项

func customCompression() {
	// 创建自定义ZIP压缩器
	z := archiver.Zip{
		CompressionLevel:       flate.BestCompression,  // 最高压缩率
		MkdirAll:              true,                   // 自动创建目录
		SelectiveCompression:  true,                   // 选择性压缩
		ContinueOnError:       false,                  // 遇到错误是否继续
		OverwriteExisting:     true,                   // 覆盖已存在文件
		ImplicitTopLevelFolder: false,                 // 是否包含顶层文件夹
	}
	
	err := z.Archive([]string{"file1.txt", "folder"}, "custom.zip")
	if err != nil {
		log.Fatal(err)
	}
}

2. 流式处理大文件

func streamExample() {
	// 创建TAR.GZ流式压缩
	tgz := archiver.NewTarGz()
	tgz.CompressionLevel = gzip.BestCompression
	
	// 创建输出文件
	out, err := os.Create("large.tar.gz")
	if err != nil {
		log.Fatal(err)
	}
	defer out.Close()
	
	// 创建压缩器
	err = tgz.Create(out)
	if err != nil {
		log.Fatal(err)
	}
	
	// 添加文件
	file1, err := os.Open("largefile1.bin")
	if err != nil {
		log.Fatal(err)
	}
	defer file1.Close()
	
	err = tgz.Write(archiver.File{
		FileInfo: archiver.FileInfo{
			Name:    "largefile1.bin",
			Mode:    0644,
			Size:    0, // 自动计算
			ModTime: time.Now(),
			IsDir:   false,
		},
		ReadCloser: file1,
	})
	if err != nil {
		log.Fatal(err)
	}
	
	// 完成压缩
	err = tgz.Close()
	if err != nil {
		log.Fatal(err)
	}
}

3. 处理密码保护的ZIP文件

func passwordProtectedZip() {
	// 创建密码保护的ZIP文件
	z := archiver.Zip{
		Password: "secret",
	}
	
	err := z.Archive([]string{"secret.txt"}, "protected.zip")
	if err != nil {
		log.Fatal(err)
	}
	
	// 解压密码保护的ZIP文件
	uz := archiver.Zip{
		Password: "secret",
	}
	
	err = uz.Unarchive("protected.zip", "extracted")
	if err != nil {
		log.Fatal(err)
	}
}

性能优化建议

  1. 对于大文件,使用流式处理
  2. 根据需求选择合适的压缩级别
  3. 批量处理文件时考虑并发

错误处理

func handleErrors() {
	err := archiver.Archive([]string{"nonexistent.txt"}, "error.zip")
	if err != nil {
		switch e := err.(type) {
		case *archiver.ErrNotArchive:
			log.Println("不是归档文件:", e)
		case *archiver.ErrNoMatch:
			log.Println("没有匹配的文件:", e)
		case *archiver.ErrIllegalPath:
			log.Println("非法路径:", e)
		default:
			log.Println("未知错误:", err)
		}
	}
}

总结

archives库为Golang提供了强大且易用的跨平台压缩和解压功能,支持多种格式,并提供了丰富的配置选项。无论是简单的压缩解压任务,还是需要精细控制的复杂场景,archives都能胜任。

回到顶部