こんにちは、株式会社カミナシのエンジニア @imuです。
はじめに
MicrosoftExcelファイル(以後、Excelという)は、切っても切れない関係ですよね。 OpenOffice、Google スプレッドシート、Numbers は、いずれもExcelとの互換性を保っています。
互換性を保っているということは、脱Excelが非常に難しくユーザーから互換機能を求められていると思います。
サービスを提供する側としても、Excelをサポートをしないと利用ユーザーの体験が悪くなり、最悪の場合はチャーンする可能性もあります。
『カミナシ』はアプリで作成したデータをExcelに出力する機能を提供しています。その際に利用したパッケージの紹介と、簡単な利用方法についてまとめてみます!
開発環境
- go (1.14.2)
Excelizeとは
Excelizeは、純粋なGoで記述されたライブラリです。
MicrosoftExcelファイル(以後、Excelという)の読み取り、書き込み、書式設定などをサポートしています。
詳しくはこちらを公式を参照してください。 github.com
Excelizeを使うとExcel操作がめちゃくちゃ簡単なのでオススメです!
インストール
go.modを利用しているため、以下の手順で行います。
go mod init hoge
すると、「go.mod」ファイルが出来ています。
go.mod
module hoge go 1.14
Excelizeの公式にある通り、Go Modulesを利用している場合は以下のコマンドを実行します。
go get github.com/360EntSecGroup-Skylar/excelize/v2
go.mod
module hoge go 1.14 require github.com/360EntSecGroup-Skylar/excelize/v2 v2.3.2 // indirect
これで準備は整いました!早速やってみましょう!
やってみよう
作成したExcelファイルの結果は全てNumbersでの表示になります。 またimportは省略してます。
ファイル作成
package main func main() { f := excelize.NewFile() if err := f.SaveAs("サンプル.xlsx"); err != nil { fmt.Println(err) } }
これだけでExcelファイルが作成出来てしまいます!めちゃくちゃ簡単…。
読み込み
- 直接ファイル名を読み込む場合
package main func main() { f, err := excelize.OpenFile("ExcelizeSample.xlsx") if err != nil { fmt.Println(err) return } // 1番目シート名を取得しています fmt.Println(f.GetSheetName(1)) }
- バイナリファイルを読み込む場合
package main func main() { b, err := ioutil.ReadFile("ExcelizeSample.xlsx") if err != nil { fmt.Println(err) return } f, err := excelize.OpenReader(bytes.NewReader(b)) if err != nil { fmt.Println(err) return } fmt.Println(f.GetSheetName(1)) }
シート操作
package main func main() { f := excelize.NewFile() // シート名変更 changeSheetName := "サンプル" f.SetSheetName("Sheet1", changeSheetName) // A1セルに値を設定 f.SetCellValue(changeSheetName, "A1", 123) // シート追加 addSheetName := "サンプル2" i := f.NewSheet(addSheetName) // シート名取得 => サンプル2 と出力される fmt.Println(f.GetSheetName(i)) // シート削除 deleteSheetName := "サンプル3" f.NewSheet(deleteSheetName) f.DeleteSheet(deleteSheetName) // シートコピー from := f.GetSheetIndex(changeSheetName) to := f.GetSheetIndex(addSheetName) if err := f.CopySheet(from, to); err != nil { fmt.Println(err) return } if err := f.SaveAs("サンプル.xlsx"); err != nil { fmt.Println(err) return } }
- 出力結果 gyazo.com
サンプル以外にも、シートを表示/非表示にしたり、シート内を検索出来るので多機能です。
セル操作
Excelizeで画像を扱う場合は、必要に応じて以下のimportを行ってください。
- _ "image/gif"
- _ "image/jepg"
- _ "image/png"
package main import ( "bytes" "fmt" "image/png" _ "image/png" "os" "github.com/360EntSecGroup-Skylar/excelize/v2" ) func main() { f := excelize.NewFile() // シート名変更 changeSheetName := "サンプル" f.SetSheetName("Sheet1", changeSheetName) // セルに値を設定 // SetCellBool, SetCellInt, SetCellStrなどがありますが、SetCellValueがinterface型なので使いやすいかなと思います f.SetCellValue(changeSheetName, "A1", 123) f.SetCellValue(changeSheetName, "A2", true) f.SetCellValue(changeSheetName, "A3", "123") // 計算式 f.SetCellValue(changeSheetName, "B1", 123) f.SetCellValue(changeSheetName, "B2", 123) f.SetCellFormula(changeSheetName, "B3", "B1+B2") // スタイル設定(フォント) // github.com/360EntSecGroup-Skylar/excelize を使う場合は、下記のように文字列で書く必要があります。 // style, err := f.NewStyle(`{"font":{"bold":true,"italic":true,"family":"Berlin Sans FB Demi","size":36,"color":"#777777"}}`) style, err := f.NewStyle(&excelize.Style{ Font: &excelize.Font{ Bold: true, Italic: true, Family: "Berlin Sans FB Demi", Size: 36, Color: "#777777", }, }) if err != nil { fmt.Println(err) return } f.SetCellStyle(changeSheetName, "B3", "B3", style) // セルの高さ、幅を設定 f.SetColWidth(changeSheetName, "B", "C", 20) f.SetRowHeight(changeSheetName, 3, 100) // 画像 if err := f.AddPicture(changeSheetName, "D1", "image.png", ""); err != nil { fmt.Println(err) return } // 元の画像をスケーリングして出力 if err := f.AddPicture(changeSheetName, "D5", "image.png", `{"x_scale": 0.5, "y_scale": 0.5}`); err != nil { fmt.Println(err) return } // 画像バイナリデータ版 image, err := os.Open("image.png") defer image.Close() if err != nil { fmt.Println(err) return } img, err := png.Decode(image) if err != nil { fmt.Println(err) return } buffer := new(bytes.Buffer) if err := png.Encode(buffer, img); err != nil { fmt.Println(err) return } if err := f.AddPictureFromBytes(changeSheetName, "D10", `{"x_scale": 0.2, "y_scale": 0.2}`, "image", ".png", buffer.Bytes()); err != nil { fmt.Println(err) return } // [x, y]座標から英数字のセル名に変換する // [1, 1] => "A1" cellName, err := excelize.CoordinatesToCellName(1, 1) if err != nil { fmt.Println(err) return } fmt.Println(cellName) // セル内の改行 f.SetCellValue(changeSheetName, "A12", "あいうえお\nかきくけこ") f.SetRowHeight(changeSheetName, 12, 60) if err := f.SaveAs("サンプル.xlsx"); err != nil { fmt.Println(err) return } }
- 出力結果
基本的には直感的に関数を使って操作が出来ると思います! 実際にExcelizeを使って実運用までに時間は掛からなかったくらい簡単です。
Tips: ファイルを開いたときに自動で再計算させる方法
最後にExcelizeを利用する上で困ったことの共有です!
既存ファイルを元に『カミナシ』で記録したデータを設定した後、別ファイルとして出力してファイルを開いたとき、セルの関数がうまく実行出来ないケースがありました。
その場合はExcelize側でFullCalcOnLoadプロパティを設定することで対応出来ました。
package main func main() { b, err := ioutil.ReadFile("ExcelizeSample.xlsx") if err != nil { fmt.Println(err) return } f, err := excelize.OpenReader(bytes.NewReader(b)) if err != nil { fmt.Println(err) return } // 『カミナシ』で記録したデータを出力... // 開いたときに自動計算する if f.WorkBook != nil && f.WorkBook.CalcPr != nil { f.WorkBook.CalcPr.FullCalcOnLoad = true } if err := f.SaveAs("サンプル.xlsx"); err != nil { fmt.Println(err) return } }
おわりに
いかがだったでしょうか。
「Excelize」を使うことでExcelを簡単に操作出来ましたね!
非常に便利なパッケージだと思いますので、Excelを利用する場合はぜひ候補の1つに加えてみるのも良いと思います!
最後に…
弊社ではエンジニアを募集しており、興味がある、話を聞いてみたい、応募したいという方はお気軽にWantedlyやTwitterのDMからお待ちしております!