はじめに
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