関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。たまにやりたいときに忘れるのでここにメモメモ。
たまーになのですが、パスワード付きのZIPファイルをGoから作成したり解凍したりしたいときがあります。
標準ライブラリの archive/zip にはパスワード付きでZIPファイルを作成する機能が無いので、以下のライブラリを利用させてもらって処理。
サンプル
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
Goのおすすめ書籍
上の書籍の日本語版が下です。
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。