おひとり

できる限りひとりで楽しむための情報やプログラミング情報など。

Golangでtarを作成する方法

f:id:hitoridehitode:20191003205941j:plain:w300
golang

GolangのDockerクライアントでコンテナにファイルを送る際、tarファイルを作成する必要があったため、調べました。

プログラム

まずは結論から、以下のようなプログラムでtarを作成出来ます。
今回のサンプルでは以下を実装してみました。

  • ディスク上のファイルをtarに追加する。
  • 変数の中のデータ(文字列)をtarに追加する。
package main

import (
    "archive/tar"
    "fmt"
    "io"
    "os"
    "time"
)

func main() {
    //1. tarファイルの作成
    tarFile, err := os.Create("dest/archive.tar")
    if err != nil {
        fmt.Println("tarファイルが作成できなかった。")
        return
    }

    //2.  tarWriterを作成
    tarWriter := tar.NewWriter(tarFile)
    defer tarWriter.Close()

    //3. ディスク上のファイルをtarに追加する。
    file, err := os.Open("src/hoge.txt")
    if err != nil {
        fmt.Println("ファイルが開けなかった。")
        return
    }

    stat, err := file.Stat()
    if err != nil {
        fmt.Println("ファイルの状態が取得できなかった。")
        return
    }

    header := &tar.Header{
        Name:    "hoge.txt", //ここでsrc/hoge.txtと指定すると、解答時にそのPATHになる。
        Mode:    int64(stat.Mode()),
        ModTime: stat.ModTime(),
        Size:    stat.Size(),
    }

    err = tarWriter.WriteHeader(header)
    if err != nil {
        fmt.Errorf("headerを書き込めなかった")
        return
    }

    _, err = io.Copy(tarWriter, file)
    if err != nil {
        fmt.Errorf("ファイルをコピー出来なかった")
        return
    }

    //4. 変数のデータをtarに追加する
    data := "Hello world!!"
    header = &tar.Header{
        Name:    "data/hello.txt",
        Mode:    0644,
        ModTime: time.Now(),
        Size:    int64(len(data)),
    }

    err = tarWriter.WriteHeader(header)
    if err != nil {
        fmt.Errorf("headerを書き込めなかった")
        return
    }

    _, err = tarWriter.Write([]byte(data))
    if err != nil {
        fmt.Println("tarに書き込めなかった")
    }
}

解説

まずは、tarファイルを作成、そのtarに書き込むためのWriterを取得します。

//1. tarファイルの作成
tarFile, err := os.Create("dest/archive.tar")
if err != nil {
  fmt.Println("tarファイルが作成できなかった。")
    return
}

//2.  tarWriterを作成
tarWriter := tar.NewWriter(tarFile)
defer tarWriter.Close()

ここまではシンプルですね。

つぎにheaderを作成してwriter経由で書き込み ます。

header := &tar.Header{
        Name:    "hoge.txt",   //解凍時のパスになる
        Mode:    int64(stat.Mode()),  // 0644など定数も指定できる。
        ModTime: stat.ModTime(),  // 最終更新時刻
        Size:    stat.Size(),  // 正しいサイズを指定する!
}

Name には、tarに格納するファイル名を指定します。
Nameにdata/hoge.txt という風にパスを指定すると、解凍時にはdataディレクトリが作成されます。
そのほか、ModeModTime など指定します。
特に、Sizeは正しく指定しないと tarWriter.Write() でエラーになります。
この辺は公式のドキュメントを参照してください。

最後に、tarに実際のデータを追加します。

// ファイルをtarの中にコピーする場合
_, err = io.Copy(tarWriter, file)
if err != nil {
    fmt.Errorf("ファイルをコピー出来なかった")
    return
}

また、変数の中身をファイルとして出力することも出来ます。

_, err = tarWriter.Write([]byte(data))

このプログラムを実行すると、 archive.tar というファイルが dest フォルダの中にできます。 これを解凍すると、以下のようになります。

.
└── archive
    ├── data
    │   └── hello.txt
    └── hoge.txt

Golangでたくさんtarを作りましょう!!!

リンク

golang.org

このプログラムのGist版
How to create tar file in Golang · GitHub