Play framework

Excel出力

Excel出力機能を実装するためのモジュールが公開されているので使ってみる。jXLSというテンプレート処理型のExcelドキュメント生成ツールを使用して、ページの出力とする仕組み。

jXLSはApacheプロジェクトのPOIを含んだツールで、効率的に帳票作成ができるように工夫されている。

xlsx形式への対応済。

jXLS自体には、POIを直接たたく術があるため、細やかな装飾を動的に行えるようになっている。しかし、Play!Excelモジュールではテンプレート処理機能しか利用できないため、より細やかな制御を行う場合は、何らかの対応が必要と思われる。

1つのシートを元にを複数のシートを増産することも非対応。このモジュールを使わずにjXLSを直接利用するのであれば"transformMultipleSheetsList"メソッドを用いれば実現できる。ただし、1ワークブックにつき1テンプレシートの増産しか出来ない。

目次

インストール

モジュールのインストールはコマンドラインから簡単に行える。ダウンロードからインストールまで自動で行ってくれる。

> play install excel-1.2.3

バージョンに'x'がついているものは、xlsx形式に対応したバージョン。 ノーマル版が3MBに対し、xlsx対応版は18MBあるため(公式ページ情報)、慎重に選んだ方がよいかと。

今回はxlsx形式を扱う予定が無かったので、ノーマル版とした。

  1. /modules配下に、excel-1.2.3というフォルダが作られる。
  2. README.textileを見ると、application.confに下記の設定を加えろ、と書いてあるが、どうもやらなくても良さそう。特に2行目は旧バージョン用の設定。
    module.Excel=${play.path}/modules/Excel
    excel.template.root=app/views
    
  3. 代わりにプロジェクトの依存性を管理するファイル、conf/dependencies.ymlを変更する。
    # Application dependencies
    
    require:
        - play
        - play -> excel 1.2.3
    
    そして、コマンドプロンプトより依存性解決を指示する。
    > play dependencies [アプリ名]
    
    アプリフォルダ内に、modulesフォルダが作成される。
  4. eclipseで開発する場合はプロジェクトファイルを再生成する。
    > play ec [アプリ名]
    

使い方

  • 仕組みとしては、事前に用意したエクセルファイルをテンプレートとして、動的にレポート作成を行う方式。
  • テンプレートの書き方は下記のリンクを参照。
  • テンプレートは、viewsフォルダ配下のhtmlと同じように配置する。
  • Excel出力する場合は下記のように、リクエストフォーマットを「xls」とし、ファイル名「__FILE_NAME__ 」を設定する。そして、パラメータがある場合は、第2引数以降に記述する。
        public static void excelOut1()
        {
            request.format = "xls";
            String __FILE_NAME__ = "sample.xls";
    
            String name = "田中";
    
            render(__FILE_NAME__, name);
        }
    render()が呼ばれると、呼び元のメソッド名から「excelOut1.xls」というテンプレートを探し、処理&出力される。

テンプレートの記法

変数の受け渡し

  • render()で渡される第2引数以降は、htmlのテンプレート処理と同じように ${} で利用できる。文字列変数nameであれば「${name}」のように。
  • Mapオブジェクトの要素へのアクセスは、ドットを用いる。
        public static void excelOut1()
        {
            request.format = "xls";
            String __FILE_NAME__ = "sample.xls";
    
            String name = "田中";
            Map obj = new HashMap();
            Map obj2 = new HashMap();
    
            obj.put("name", "すずき");
            obj.put("address", "東京都");
            obj2.put("name", "さとう");
            obj2.put("address", "さいたま");
            obj.put("omake", obj2);
    
            render(__FILE_NAME__, name, obj);
        }
    これに対し用意したテンプレートはこちら。
    1.png
    これらを基に出力されたExcelがこちら。
    2.png
    気づきましたか? 4行目の記述で${obj.okame.name}とミスタイプしてるんですが、${name}相当だと勝手に解釈してしまってます。エラーにならないので気を付けないと、ですね。(ログには、WARNレベルで出力されてます)
  • 「=10*5」という内容の文字列を渡した場合、式を評価する前の状態で出力される。Excel上で再編集を行うことで「50」と表示される。
  • A1セルに ${value1}、A2セルに ${value2}として、A3セルに「=A1+A2」と記述した場合、テンプレートの時点でA3セルはエラー。テンプレート処理後もA3セルはエラーとして出力される。Excel上で再編集を行うと計算が行われ、望む結果が表示される。

数式の埋め込み

  • $[ ]を用いる。
  • カッコ内に記述した式がそのまま出力される。$[10+5]とすれば、セルには「=10+5」と出力される。
  • セル同士の演算も同じ。$[A1+A2+A5]とすれば、「=A1+A2+A5」とセルに出力される。
  • SUMなどの関数も同様。繰返し処理で増えるセルの場合であっても、$[SUM(A5)]のようにそのセルを直指定すれば動的に対応してくれる。便利。

繰返し処理

  • jx:forEachタグを用いると、コレクションを繰返し表示するような処理が行える。
  • jx:forEachタグでいくつかの行を挟み、挟まれた行が繰返し処理分、複製される仕組み。
  • 書式
    <jx:forEach items="${コレクション名}" var="処理用の名前">
         繰返したい行を挟む
    </jx:forEach>
    

繰返し処理+グループごと

  • jx:forEachタグで "groupBy" を用いると、指定したプロパティでグループ化したコレクション(= group.items) が得られる。これを利用してグループ毎の繰返し処理を行う。
  • 書式
    <jx:forEach items="${コレクション名}" groupBy="グループ化したいプロパティ名">
         ${group.item.グループ化したいプロパティ名}でグループ化
    <jx:forEach items="${group.items}" var="処理用の名前">
         繰返したい行を挟む
    </fx:forEach>
    </fx:forEach>
    
  • 抽出されるグループの出力順は、コレクションに格納されている順番に依存する。

繰返し処理+フィルタ

  • jx:forEachタグでコレクションを処理するに当たり、条件を満たさないものを対象外にすることができる。
  • "select" を使用する。
  • 書式例
    <jx:forEach items="${items}" var="item" select="${item.address != "とうきょう"}">
    

繰返し処理+カウンタ

  • jx:forEachタグでコレクションを処理するに当たり、何番目の処理なのか知る方法として、"varStatus"が用意されている。
  • ゼロスタート。必要なら+1しよう。
  • 書式例
    <jx:forEach items="${items}" var="item" varStatus="x">
        ${x.index +1}番目の処理です。
    </jx:forEach>
    

条件

  • jx:ifタグで囲むことで、条件を満たすときだけ出力される行を設けることができる。
  • "test"に評価式を指定する。
  • 書式例
    <jx:if test="${item.name == "すずき"}">
        鈴木?
    </jx:if>
    

グループ化(アウトライン)

  • 幾つかの行の表示/非表示を切り替えるアウトライン機能を適用する。
  • jx:outlineタグを用いる。
  • 非表示が初期状態。
  • 書式
    <jx:outline>
      見ないでください
    </jx:outline>
    

テンプレートの記法(POIオブジェクト使用)

  • 使用可能オブジェクト
    workbookカレントのorg.apache.poi.ss.usermodel.Workbookオブジェクトのインスタンス
    sheetカレントのorg.apache.poi.ss.usermodel.Sheetオブジェクトのインスタンス
    hssfRowカレントのorg.apache.poi.ss.usermodel.Rowオブジェクトのインスタンス
  • テンプレート処理後は、記したセルは空セルになる。印刷欄外に書くとよさげ。

ヘッダーとフッター

  • ヘッダーやフッターに、${...}といった記述をしても評価の対象外。
  • POIオブジェクトを利用してヘッダーやフッターの内容を動的に変更する。
  • 書式例(ヘッダー左の内容を変更する)
    ${sheet.getHeader().setLeft('Left Header')}
    
    仮に、nameという引数があるなら下記のように指定する。
    ${sheet.getHeader().setLeft(name)}
  • 置換用記号を使ってExcelでお馴染みの設定ができる。
    置換用記号Excelでの表記
    &P&[ページ番号]
    &N&[総ページ数]
    &D&[日付]
    &T&[時刻]
    &F&[ファイル名]
    &A&[シート名]
  • フォント設定などは直接記述する。
    ${sheet.getFooter().setLeft('&"HGP教科書体,メディウム 斜体"&18あいうえお')}
  • 手でフッタヘッダを作成した後に getLeft()などの取得メソッドを使って値を見る方が早いと思います。

ワークシートの名前を変える

  • workbook.setSheetNameメソッドを使用する。
  • 第1引数はシート番号、第2引数は変更する名前。
  • シート番号はゼロ開始。
    ${workbook.setSheetName(0, '新しい名前')}
    

アクティブシートの変更

  • workbook.setActiveSheetメソッドを使用する。
  • シート番号はゼロ開始。
    ${workbook.setActiveSheet(1)}
    

ワークシートの削除

  • workbook.removeSheetAtメソッドを使用する。
  • シート番号はゼロ開始。
    ${workbook.removeSheetAt(1)}
    

シート選択で最初に表示されるシートを指定

  • Excelでシート下部に表示されるシート選択用のタブについて、どのシートをメインに表示するかを指定できる。
  • workbook.setFirstVisibleTabメソッドを使用する。
  • シート番号はゼロ開始。
    ${workbook.setFirstVisibleTab(1)}
    

シート並び順の変更

  • workbook.setSheetOrderメソッドを使用する。
  • 第1引数は移動対象となるシートの名前、第2引数は移動先。
  • 移動先は、どのシートの前に挿入するか、で指定。シート番号はゼロ開始。
    ${workbook.setSheetOrder(workbook.getSheetName(0),2)}
    

シートの拡大率

  • sheet.setZoomメソッドを使用する。
  • 第1引数÷第2引数=拡大率、となる。75%なら3と4。150%なら3と2のように指定。
    ${sheet.setZoom(3,4)} 

POIについてはコチラの書籍が参考になります。

良かったらポチってください。

強制改ページの設定

  • sheet.setRowBreak、sheet.setColumnBreakメソッドを使用する。
  • テンプレート処理中の行で設定されるため、forEachなどで行増殖中や前に設定すると意図した結果が得られないことがある。
    ${sheet.setRowBreak(item.id/12*30)}

気づいたことなど

  • オートシェイプについて
    • jXLSで処理して行数などが変わっても位置は動きません。Excelではオブジェクトのプロパティでセルに合わせて移動したり固定としたり設定が可能ですが、無視されます。テンプレートで配置した位置から動きません。
    • jXLSでループ展開の影響を受けません。上記と被りますが、オートシェイプをループ処理で挟んでも展開されません。ループ処理とは無関係にテンプレートで配置した位置に出力されます。
  • 改ページについてもループ展開されません。テンプレートに設定した改ページは増えたりせずそのまま出力されます。
  • jXLSのループ処理はコレクションありきなので、n回分の生成を行いたいときは不便です。その場合はn個のダミー配列を作成して引数として渡すとテンプレートがすっきりします。メモリと相談ですが。
  • シートの保護などもテンプレート処理後も引き継いでくれる。但し、保護されたシートを"transformMultipleSheetsList"メソッドでシートを増産した場合は、オリジナルシートのみ保護されている状態となる。都合が悪い場合は、自前で増産されたシートにも保護をかけていく必要がある。

リンク

http://www.playframework.org/modules/excel
公式サイトにあるモジュールの窓口(英語)
http://jxls.sourceforge.net
excelのテンプレートファイルの書き方について
http://ameblo.jp/kozake/entry-11110864557.html
「システムアーキテクトのごった煮」より「ExcelをPlay frameworkで扱うお話」
http://groups.google.com/group/play-framework
GoogleのPlay!専用会議室

添付ファイル: file1.png 658件 [詳細] file2.png 598件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2012-12-14 (金) 18:22:35 (2376d)