2
0

More than 3 years have passed since last update.

Windowsで文字化けしない zip ファイルを golang で作る

Posted at

はじめに

MacやLinuxでZIPファイルを作るとWindowsで文字化けする場合があります。
Shift-JISに対応したソフトを使用すれば良いのですが今回は golang でささっと圧縮プログラムを作ってみます。

やってみよう

必用なもの

  • golang

仕様

  • CLIコマンド
  • 入力文字コードはUTF8固定(Mac、Linux共にUTF8がデフォルト文字コード)
  • パラメータで圧縮対処フォルダを指定する
  • Windowsで文字化けしないようにファイル/フォルダ名をCP932に変換して保存する

実装

まずはUTF8文字列 -> Shift-JIS文字列の変換関数を用意します。

main.go
func Utf82Sjis(str string) (string, error) {
    iostr := strings.NewReader(str)
    rio := transform.NewReader(iostr, japanese.ShiftJIS.NewEncoder())
    ret, err := ioutil.ReadAll(rio)
    if err != nil {
        return "", err
    }
    return string(ret), err
}

次にZIPファイルのヘッダを作成する関数を用意します。
ヘッダ作成時にファイルのタイムスタンプも修正します。

main.go
func FileInfoHeader(fi os.FileInfo) (*zip.FileHeader, error) {
    size := fi.Size()
    fh := &zip.FileHeader{
        Name:               fi.Name(),
        UncompressedSize64: uint64(size),
    }

    local := time.Now().Local()
    _, offset := local.Zone()
    fh.SetModTime(fi.ModTime().Add(time.Duration(offset) * time.Second))
    fh.SetMode(fi.Mode())
    var uintmax = uint32((1 << 32) - 1)
    if fh.UncompressedSize64 > uint64(uintmax) {
        fh.UncompressedSize = uintmax
    } else {
        fh.UncompressedSize = uint32(fh.UncompressedSize64)
    }
    return fh, nil
}

最後に指定フォルダ配下をZIPする関数を用意します。

main.go
func Zip(source, output string) error {
    var f *os.File
    if "-" == output {
        f = os.Stdout
    } else {
        var err error
        f, err = os.Create(output)
        if err != nil {
            return err
        }
    }
    defer f.Close()

    archive := zip.NewWriter(f)
    defer archive.Close()

    info, err := os.Stat(source)
    if err != nil {
        return err
    }

    var baseDir string
    if info.IsDir() {
        baseDir = filepath.Base(source)
    }

    filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        header, err := FileInfoHeader(info)
        if err != nil {
            return err
        }

        if baseDir != "" {
            //header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
            name, err := Utf82Sjis(filepath.Join(baseDir, strings.TrimPrefix(path, source)))
            if err != nil {
                return err
            }
            header.Name = name
        }

        if info.IsDir() {
            header.Name += "/"
        } else {
            //header.Method = zip.Deflate
            header.Method = zip.Store
        }

        writer, err := archive.CreateHeader(header)
        if err != nil {
            return err
        }

        if info.IsDir() {
            return nil
        }

        file, err := os.Open(path)
        if err != nil {
            return err
        }
        _, err = io.Copy(writer, file)
        file.Close()
        return err
    })

    return err
}

あとは作ったZip関数を実行する main() を作れば完了。

main.go
func main() {
    if 3 > len(os.Args) {
        fmt.Printf("usage:\n\t%s [zip file] [archive directory]\n", os.Args[0])
        os.Exit(1)
    }

    output := os.Args[1]
    source := os.Args[2]

    if err := Zip(source, output); err != nil {
        panic(err)
    }
}

今回のコード一式は以下にあります。
やっぱツール作るなら golang がお気に入りです。
https://github.com/noridas80/zi18np

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0