All formats

sevenzip (Go)

LanguageGo
LicenseBSD 3-clause
Repositorygithub.com/bodgit/sevenzip
Documentationpkg.go.dev/github.com/bodgit/sevenzip
Depends onpure Go (uses Go compression packages for LZMA, Zstandard, Brotli, LZ4 and others)
Read / writeyes / no; LZMA, LZMA2, PPMd, BZip2, Deflate, Zstandard, Brotli, LZ4, Copy, Delta, BCJ, BCJ2, ARM, ARM64, PPC, SPARC; AES-256

sevenzip reads 7z archives with an API modeled on archive/zip: open the archive, loop over r.File, call f.Open() to get an io.ReadCloser. It handles compressed and encrypted headers, multi-volume archives (.7z.001) and self-extracting .exe archives, verifies CRC-32 values, and implements fs.FS so you can use an archive with fs.WalkDir, http.FileServer or template.ParseFS.

It only reads. Files in a 7z folder (solid block) share one compressed stream; the library caches the stream so that extracting files in archive order is fast, as long as you close each file before opening the next.

Installation#

Shell
go get github.com/bodgit/sevenzip

Basic usage: extract a 7z archive#

Go
package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/bodgit/sevenzip"
)

func extractFile(f *sevenzip.File, dest string) error {
	rc, err := f.Open()
	if err != nil {
		return err
	}
	defer rc.Close() // close before opening the next file: keeps the stream cache working

	path := filepath.Join(dest, f.Name)
	if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) {
		return fmt.Errorf("illegal file path: %s", f.Name)
	}
	if f.FileInfo().IsDir() {
		return os.MkdirAll(path, 0o755)
	}
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return err
	}
	out, err := os.Create(path)
	if err != nil {
		return err
	}
	defer out.Close()
	_, err = io.Copy(out, rc)
	return err
}

func main() {
	r, err := sevenzip.OpenReader("archive.7z")
	if err != nil {
		log.Fatal(err)
	}
	defer r.Close()

	for _, f := range r.File {
		fmt.Printf("%s  %d bytes  %s\n", f.Name, f.UncompressedSize, f.Modified)
		if err := extractFile(f, "output"); err != nil {
			log.Fatal(err)
		}
	}
}

How to open a password-protected 7z archive#

Go
r, err := sevenzip.OpenReaderWithPassword("secret.7z", "password")
if err != nil {
	var e *sevenzip.ReadError
	if errors.As(err, &e) && e.Encrypted {
		log.Fatal("wrong or missing password")
	}
	log.Fatal(err)
}
defer r.Close()

A wrong password can’t always be detected reliably (AES-CBC has no check value). ReadError.Encrypted is a hint; a CRC mismatch while reading a file is the other sign.

How to read a 7z archive from memory or an io.ReaderAt#

Go
data, err := os.ReadFile("archive.7z") // or bytes from a download
if err != nil {
	log.Fatal(err)
}
r, err := sevenzip.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
	log.Fatal(err)
}
for _, f := range r.File {
	fmt.Println(f.Name)
}

NewReaderWithPassword(r, size, password) is the encrypted variant.

How to use a 7z archive as an fs.FS#

Go
r, err := sevenzip.OpenReader("site.7z")
if err != nil {
	log.Fatal(err)
}
defer r.Close()

// read one file by path
b, err := fs.ReadFile(r, "docs/readme.txt")

// walk all files
fs.WalkDir(r, ".", func(path string, d fs.DirEntry, err error) error {
	fmt.Println(path)
	return err
})

How to open a multi-volume archive#

Pass the first volume; the library finds .002, .003 and so on:

Go
r, err := sevenzip.OpenReader("backup.7z.001")
if err != nil {
	log.Fatal(err)
}
defer r.Close()
fmt.Println(r.Volumes()) // all volume file names

Tips#