【Golang + Excelize】Excelファイルを操作してみよう

f:id:kaminashi-developer:20210124184158j:plain

こんにちは、株式会社カミナシのエンジニア @ImuKnskです。

はじめに

MicrosoftExcelファイル(以後、Excelという)は、切っても切れない関係ですよね。 OpenOfficeGoogle スプレッドシート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ファイルが作成出来てしまいます!めちゃくちゃ簡単…。

f:id:kaminashi-developer:20210124161333p:plain

読み込み
  • 直接ファイル名を読み込む場合
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
    }
}

サンプル以外にも、シートを表示/非表示にしたり、シート内を検索出来るので多機能です。

セル操作

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
    }
}
  • 出力結果 f:id:kaminashi-developer:20210124180535p:plain

基本的には直感的に関数を使って操作が出来ると思います! 実際に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つに加えてみるのも良いと思います!

最後に…

弊社ではエンジニアを募集しており、興味がある、話を聞いてみたい、応募したいという方はお気軽にWantedlyTwitterのDMからお待ちしております!