いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-381 (パスワード付きZIPファイルを作成・解凍)

関連記事

devlights.hatenablog.com

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。たまにやりたいときに忘れるのでここにメモメモ。

たまーになのですが、パスワード付きのZIPファイルをGoから作成したり解凍したりしたいときがあります。

標準ライブラリの archive/zip にはパスワード付きでZIPファイルを作成する機能が無いので、以下のライブラリを利用させてもらって処理。

github.com

サンプル

package main

import (
    "bytes"
    "fmt"
    "io"
    "log"
    "os"
    "strings"

    "github.com/yeka/zip"
)

var (
    password = os.Getenv("ZIP_PASS")
)

func main() {
    if strings.TrimSpace(password) == "" {
        fmt.Fprintln(os.Stdout, "環境変数 ZIP_PASS を設定して実行してください")
        os.Exit(1)
    }

    var err error
    if err = run(); err != nil {
        panic(err)
    }
}

func run() error {
    var (
        zipPath string
        err     error
    )

    zipPath, err = _zip()
    if err != nil {
        return fmt.Errorf("zip() failed: %w", err)
    }

    err = _unzip(zipPath)
    if err != nil {
        return fmt.Errorf("unzip() failed: %w", err)
    }

    return nil
}

func _zip() (string, error) {
    const (
        ZIP_PATH = "./test.zip"
    )

    var (
        zipFile *os.File
        err     error
    )

    zipFile, err = os.Create(ZIP_PATH)
    if err != nil {
        return "", fmt.Errorf("os.Create() failed: %w", err)
    }

    var (
        zipW = zip.NewWriter(zipFile)
        w    io.Writer
    )
    defer zipW.Close()

    w, err = zipW.Encrypt("test.txt", password, zip.AES256Encryption)
    if err != nil {
        return "", fmt.Errorf("zip.Writer.Encrypt() failed: %w", err)
    }

    var (
        contents = bytes.NewBufferString("こんにちは世界")
    )

    _, err = io.Copy(w, bytes.NewReader(contents.Bytes()))
    if err != nil {
        log.Fatal(err)
    }

    err = zipW.Flush()
    if err != nil {
        return "", fmt.Errorf("zip.Writer.Flush() failed: %w", err)
    }

    return ZIP_PATH, nil
}

func _unzip(p string) error {
    var (
        zipR *zip.ReadCloser
        err  error
    )

    zipR, err = zip.OpenReader(p)
    if err != nil {
        return fmt.Errorf("zip.OpenReader() failed: %w", err)
    }
    defer zipR.Close()

    var (
        files = zipR.File
        errCh = make(chan error, len(files))
    )

    for _, f := range files {
        if f.IsEncrypted() {
            f.SetPassword(password)
        }

        func(f *zip.File) {
            var (
                fi = f.FileInfo()
                r  io.ReadCloser
                w  io.WriteCloser
            )

            r, err = f.Open()
            if err != nil {
                errCh <- fmt.Errorf("zip.File.Open() failed: %w", err)
                return
            }
            defer r.Close()

            w, err = os.Create(fi.Name())
            if err != nil {
                errCh <- fmt.Errorf("os.Create() failed: %w", err)
                return
            }
            defer w.Close()

            _, err = io.Copy(w, r)
            if err != nil {
                errCh <- fmt.Errorf("io.Copy() failed: %w", err)
                return
            }
        }(f)
    }

    close(errCh)

    for e := range errCh {
        return e
    }

    return nil
}

実行すると、test.zip が出来上がります。

$ ZIP_PASS=helloworld go run main.go

$ file ./test.zip
./test.zip: Zip archive data, at least v2.0 to extract, compression method=AES Encrypted

linuxの標準unzipコマンドは、AESに対応していないので、別途 7zip をインストール。

$ sudo apt install -y p7zip-full

んで、解凍してみます。

gitpod /workspace/gotmp (main) $ 7z e ./test.zip

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,16 CPUs AMD EPYC 7B13 (A00F10),ASM,AES-NI)

Scanning the drive for archives:
1 file, 209 bytes (1 KiB)

Extracting archive: ./test.zip
--
Path = ./test.zip
Type = zip
Physical Size = 209

    
Enter password (will not be echoed):
Everything is Ok

Size:       21
Compressed: 209

ちゃんとパスワード付いていますね。

参考情報

zip package - archive/zip - Go Packages

GitHub - yeka/zip: Fork of Go's archive/zip to add reading/writing of password protected zip files.

unzipコマンドで unsupported compression method 99 というエラー - suzuki-navi’s blog

Decompression of zip file fails with error "unsupported compression method 99" on Red Hat Enterprise Linux 5 - Red Hat Customer Portal

Goのおすすめ書籍

上の書籍の日本語版が下です。


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。