Migaro. 技術Tips

                       

ミガロ. 製品の技術情報
IBMiの活用に役立つ情報を掲載!


ブラウザ側のJavaScriptでExcelを出力する方法(クライアントサイド)

JavaScriptは年々進化しており、様々なことがクライアントサイドのJavaScriptでもできるようになっています。Tipsでは、Excelを出力することのできるExcelJSを使用して、ブラウザ側のhtmlとJavaScriptでExcelを操作/出力する方法をご紹介します。

はじめに

クライアントサイドでのExcel出力について、弊社テクニカルレポートに詳細がまとめられています。
こちらも参考ください。
https://www.migaro.co.jp/contents/support/technical_report_search/no16/tech/16_01_04.pdf

ExcelJSとは

ExcelJSはNode.jsで使用できるパッケージでnpm※1に公開されています。
このパッケージを使用すると、ExcelファイルをJavaScriptで簡単に操作することができます。Node.jsのパッケージですが、Browserify※2を使用することでNode.js環境だけではなく、ブラウザ側(クライアントサイドJavaScript)のみでも、ExcelJSを使用することができます。

※1. npmとは、npmはノードパッケージマネージャーの略で、パッケージ単位にまとめられた、JavaScriptの各種ライブラリの依存関係を管理してくれるシステムです。
Node.jsにはnpmが同梱されています。

※2.Browserifyとは、require等、Node.jsで使用する記述や、npmのパッケージをブラウザ側で利用できるようにするパッケージです。

ExcelJSの入手

npmからExcelJSを入手します。
npmが必要になるため、Node.jsの公式サイトからインストーラをダウンロードして、インストーラからNode.jsをインストールしてください。

Node.jsの公式サイト

◆Node.js
https://nodejs.org/ja/

インストール自体は、インストーラーを実行するだけのため、簡単に導入することができます。
インストーラにnpmも同梱されているため、同時にインストールされます。

npmからExcelJSパッケージの入手

今回ブラウザ側で使用する、ExcelJSのJavaScriptファイルを入手します。
npmからダウンロードするExcelJSのパッケージには、browserifyでまとめられたJavaScriptファイルが含まれています。
クライアントサイド(ブラウザ側)でExcelJSを使用する場合には、browserifyでまとめられたJavaScriptファイル(dist/exceljs.js)を使用します。

npmの実行
npmの実行

コマンドプロンプトを起動後、npmと入力してください。
コマンドの使用方法が画面に表示されれば、npmが導入されています。
Tipsでは、Node.jsを使用せず、ExcelJSのパッケージをダウンロードするためにnpmを使用しますので、ファイルをダウンロードするフォルダへ、カレントディレクトリを設定してください。

例) C:\temp\js\

npmからExcelJS取得

次に、以下のコマンドでパッケージを取得します。

npm install exceljs

カレントディレクトリにたくさんのファイルがダウンロードされますが、ブラウザ側で使用するJavaScriptファイルは一つです。
ExcelJSはパッケージにブラウザ側で使用するため、Browserifyが適用されたexceljs.jsファイルが含まれています。

node_modules/exceljs/dist/exceljs.min.js を取得してサーバーに配置ください。
Tipsでは、HTMLと同階層にjsフォルダを作成して配置しました。

Browserfyが適用されたexceljs

JavaScriptでExcel出力

Tipsでは、Webサーバー上に配置したExcelファイルをテンプレートとして、ブラウザ側のJavaScriptでテンプレートのExcelファイルを読込み、読込んだExcelファイルにSmartPad4iで出力したテーブルデータを、ExcelJSで書込む手順をご紹介します。

テンプレートのExcelファイル作成

テンプレートのExcelファイルを作成します。
Tipでは、Excelで以下画像のようなExcelファイルをtemplate.xlsxという名前で作成しました。
作成した、template.xlsxはアプリのhtmlと同階層の配置しています。

Excelのテンプレート

ExcelJSの設定

ExcelJSを使用するために、HTML側に外部スクリプトの読込みを追加します。
polyfill.jsはブラウザの仕様の違いを吸収するためのJavaScriptになります。
外部読込みのScriptはhtml内のどこに追加しても動作しますが、</body>前に記述することをお勧めします。

  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.js"></script> 
  <script src="../../../smartpad4i/html/SP4IREP21/js/exceljs.js"></script>

HTMLの編集 テーブルタグの設定

テーブルのデータを取得するため、タグにカスタムデータ属性を追加します。
以前のTipsと同じでテーブルタグにカスタムデータ属性を追加しています。
https://www.migaro.co.jp/tips/1375/#toc4

Excelダウンロードボタンの配置

Excelのダウンロード用のボタンを追加します。
例では、アンカータグを使用しています。

ボタンの追加


カスタムデータ属性でdata-exceldownload=”true”を設定することでJavaScriptからボタンが認識できるようにしています。

<a class="button is-info load-b ml-1" data-exceldownload="true" >
<i class="fa fa-file-excel"></i>Excel</a>

ExcelJSによる出力

出力実行時の動画は以下になります。

ボタンクリック時にExcelファイルを出力します。
JavaScriptの例は以下です。

      // ボタンクリック時の処理
      $('[data-exceldownload]').on('click',function(){
        createExcel();
      });
      
      // テンプレートのExcelファイルパス
      const EXCEL_URL = '/smartpad4i/html/SP4IREP21/template.xlsx'; 
      
      // 非同期関数
      async function createExcel(){
        // テンプレートのExcelファイルを読込み、バイト配列で取得
        const existingExcelBytes = await fetch(EXCEL_URL).then(res => res.arrayBuffer());
        // バイト配列で取得したExcelファイルを8ビット符号なし整数値の配列に変換
        const exceldata = new Uint8Array(existingExcelBytes);
        // ExcelBookの作成
        const workbook = new ExcelJS.Workbook();
        // テンプレートのExcelファイルのデータを読込み
        await workbook.xlsx.load(exceldata);
        
        // テーブルのデータ取得 data配列にデータを取得
        let id_arr   = $.makeArray($('[data-dwn-field="userid"]'));
        let name_arr = $.makeArray($('[data-dwn-field="username"]'));
        let dep_arr  = $.makeArray($('[data-dwn-field="userdep"]'));
        let auth_arr = $.makeArray($('[data-dwn-field="userauth"]'));
        let date_arr = $.makeArray($('[data-dwn-field="userdate"]'));
        let data = [];
        let loopCount = $('[data-dwn-field="userid"]').length;
        for(var i = 0 ; i < loopCount ; i++){
           var rec = {};
           rec.id = id_arr[i].innerText;
           rec.name = name_arr[i].innerText;
           rec.dep  = dep_arr[i].innerText;
           rec.auth = auth_arr[i].innerText;
           rec.date = date_arr[i].innerText;
           data.push(rec);
        }
        
        // テンプレートで設定したシート取得
        const worksheet = workbook.getWorksheet('UserList');
        // シートの用紙サイズ、縦横設定
        worksheet.pageSetup = { paperSize: 9, orientation:'portrait'};
        // データを書込む起点設定
        const startRow = 4;
        const startCol = 2;        
        let row = {};
        let rownum = 0;
        // Excelの開始行をデータの数コピー
        worksheet.duplicateRow(startRow , data.length - 1 , false);
        
        // シートに書き込み
        for (const rec of data) {         
          let pos = startRow + rownum;
          row = worksheet.getRow(pos);
          console.log(rec);
          row.getCell(startCol).value     = rec.id;
          row.getCell(startCol + 1).value = rec.name;
          row.getCell(startCol + 2).value = rec.dep;
          row.getCell(startCol + 3).value = rec.auth;
          row.getCell(startCol + 4).value = rec.date;
          rownum++;
        } 
        
        // 書込みした内容をwriteBufferメソッドで出力
        const uint8Array = await workbook.xlsx.writeBuffer();
        // Blobオブジェクトの作成
        const blob = new Blob([uint8Array], {type: 'application/octet-binary'});
        // ダウンロードするためにblobのURL作成
        const url = window.URL.createObjectURL(blob);
        // アンカーを作成してURL設定
        const a = document.createElement('a');
        a.href = url;
        // ダウンロードするファイル名を指定してダウンロード
        a.download = 'ユーザー一覧.xlsx';
        a.click();
        a.remove();
      }     

ボタンクリック時の処理

      // ボタンクリック時の処理
      $('[data-exceldownload]').on('click',function(){
        createExcel();
      });

Excelをダウンロードするボタンには、カスタム属性 data-exceldownload を設定しています。
ボタンクリック時に非同期関数として定義したcreateExcelを呼び出してExcelを出力します。

非同期関数とawait

      // 非同期関数
      async function createExcel(){

非同期関数は処理に時間がかかる処理の場合等に定義します。
functionの前にasyncを記述するだけです。
また、非同期関数内部でのみ、await が使用できます。
awaitを使用することで、処理の終了を待機(同期的に処理)することができます。

テンプレートの読込み

        // テンプレートのExcelファイルを読込み、バイト配列で取得
        const existingExcelBytes = await fetch(EXCEL_URL).then(res => res.arrayBuffer());
        // バイト配列で取得したExcelファイルを8ビット符号なし整数値の配列に変換
        const exceldata = new Uint8Array(existingExcelBytes);
        // ExcelBookの作成
        const workbook = new ExcelJS.Workbook();
        // テンプレートのExcelファイルのデータを読込み
        await workbook.xlsx.load(exceldata);

EXCEL_URLには、Webサーバー上に配置したテンプレートのExcelファイル(template.xlsx)のパスが設定されています。

fetchはHTTPリクエストを送信するAPIです。
HTTPリクエストの処理はサーバーへのリクエスト処理後、HTTPレスポンスを取得するまでに時間が掛かる場合があります。
awaitを設定して呼び出すことで、HTTPレスポンスが返却されるまでの処理を同期的に実行できます。

アロー関数

「=>」の記述は アロー関数と呼ばれる記述です。

        res => res.arrayBuffer()

簡単な例としては、上記の記述は以下と同じです。

        function(res){ 
          res.arrayBuffer();
        }
Excelの作成

resにHTTPレスポンスが返却されるため、arrayBufferメソッドでバイナリデータを取得します。
取得したバイナリデータはUint8Arrayを使用して、JavaScriptで扱いやすいデータに変更しています。

        // ExcelBookの作成
        const workbook = new ExcelJS.Workbook();
        // テンプレートのExcelファイルのデータを読込み
        await workbook.xlsx.load(exceldata);

Excelの作成は、ExcelJSのWorkbookメソッドで行えます。
workbookを作成後、ファイルのI/O処理を行うオブジェクトxlsxのloadメソッドでworkbookに読込ます。

テーブルに出力されたデータの読込み
        // テーブルのデータ取得 data配列にデータを取得
        let id_arr   = $.makeArray($('[data-dwn-field="userid"]'));
        let name_arr = $.makeArray($('[data-dwn-field="username"]'));
        let dep_arr  = $.makeArray($('[data-dwn-field="userdep"]'));
        let auth_arr = $.makeArray($('[data-dwn-field="userauth"]'));
        let date_arr = $.makeArray($('[data-dwn-field="userdate"]'));
        let data = [];
        let loopCount = $('[data-dwn-field="userid"]').length;
        for(var i = 0 ; i < loopCount ; i++){
           var rec = {};
           rec.id = id_arr[i].innerText;
           rec.name = name_arr[i].innerText;
           rec.dep  = dep_arr[i].innerText;
           rec.auth = auth_arr[i].innerText;
           rec.date = date_arr[i].innerText;
           data.push(rec);
        }

テーブルの各列には、カスタムデータ属性を設定しています。
カスタムデータ属性を使用して、配列データを取得後、recオブジェクトをdata配列に追加していきます。

行のコピー処理
        // テンプレートで設定したシート取得
        const worksheet = workbook.getWorksheet('UserList');
        // シートの用紙サイズ、縦横設定
        worksheet.pageSetup = { paperSize: 9, orientation:'portrait'};
        // データを書込む起点設定
        const startRow = 4;
        const startCol = 2;        
        let row = {};
        let rownum = 0;
        // Excelの開始行をデータの数コピー
        worksheet.duplicateRow(startRow , data.length - 1 , false);

workbookに読み込んだExcelファイルから、getWorksheetメソッドを使用して、シートのデータを取得します。
worksheetオブジェクトのpageSetupメソッドで、用紙サイズ(paperSize)と縦横(orientation)を指定しています。
用紙サイズは以下URLのExample Paper size を確認してください。
https://github.com/exceljs/exceljs#page-setup

4行目をコピー

次に、worksheet内のduplicateRowメソッドを実行することで、シートの4行目をデータ分下行にコピーしています。
上記画像のように、枠線がデータ行数分コピーされます。

ワークシートへの書込み
        // シートに書き込み
        for (const rec of data) {         
          let pos = startRow + rownum;
          row = worksheet.getRow(pos);
          console.log(rec);
          row.getCell(startCol).value     = rec.id;
          row.getCell(startCol + 1).value = rec.name;
          row.getCell(startCol + 2).value = rec.dep;
          row.getCell(startCol + 3).value = rec.auth;
          row.getCell(startCol + 4).value = rec.date;
          rownum++;
        } 

data配列に格納されたデータをループ処理で取り出します。
worksheetのgetRowメソッドを使用して行のオブジェクトを取得後、getCellメソッドで列番号のセルを取得して値を書き換えます。

xlsxファイルを作成してダウンロード
        // 書込みした内容をwriteBufferメソッドで出力
        const uint8Array = await workbook.xlsx.writeBuffer();
        // Blobオブジェクトの作成
        const blob = new Blob([uint8Array], {type: 'application/octet-binary'});
        // ダウンロードするためにblobのURL作成
        const url = window.URL.createObjectURL(blob);
        // アンカーを作成してURL設定
        const a = document.createElement('a');
        a.href = url;
        // ダウンロードするファイル名を指定してダウンロード
        a.download = 'ユーザー一覧.xlsx';
        a.click();
        a.remove();

workbookのxlsxオブジェクトのwriteBufferメソッドで、unit8Array形式のデータが取得できます。
BlobオブジェクトにUint8Arrayを渡してblobオブジェクトを作成します。
次に、ダウンロードをするために、window.URL.createObjectURLメソッドでURLを取得して、アンカータグのhrefプロパティに設定して、アンカーのdownloadプロパティにダウンロードのファイル名を指定後クリックすることでファイルをダウンロードします。

以上で、Excel出力のためのJavaScript説明は終了です。

おわりに

JavaScriptの進化によって、ブラウザ側(クライアントサイド)のJavaScriptのみで、Excelファイルの操作/出力ができるようになりました。
SmartPad4i / Cobos4i のアプリケーションでExcel帳票を作成するような場合に是非ご活用ください。