Help us understand the problem. What is going on with this article?

JavaScriptでExcel(XLSX)を読み込む

More than 3 years have passed since last update.

概要

前回HTMLのTABLEタグをExcel(XLSX)に出力する方法を投稿したんですが、ここ最近業務の効率化でJavaScriptでExcelを読み込むという方法を使えないかと思ったら、すぐに情報が出てきたので、まとめておきます。

ちなみにExcelを使わないという選択肢も効率化の中にはあるのですが、それはまた別の機会に取り組みます。
とりあえず今回のテーマはJavaScriptでExcelを読み込むです。

Sheet.js

http://sheetjs.com/

今回も必要なものはSheetJSです。
フルパッケージのスクリプトを読み込めば必要なものはすべて入っています。
今回もCDNから取得しました。

構成

オブジェクトを作ったわけなんですが、基本的には参考のページに書いてあることを踏襲しているだけです。

実装

この辺りを参考に作ります。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title></title>
    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  </head>
  <body>
    <form action="" method="get">
      <input type="file" name="upload_file" id="import-excel" />
      <h2>Result</h2>
      <div id="result"></div>
    </form>

    <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
    <!-- Latest compiled and minified JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.11.1/xlsx.full.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>
script.js
(function (window, document) {
  window.ExcelJs = {};
  ExcelJs.File = function (_file, _workbook) {
    var that = this;
    var file = _file;
    var workbook = _workbook;

    return {
      getFile: function () {
        return file;
      },
      getWorkbook: function () {
        return workbook;
      },
      toJson: function () {
        var result = {};
        workbook.SheetNames.forEach(function(sheetName) {
          var roa = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);
          if(roa.length > 0){
            result[sheetName] = roa;
          }
        });
        return result;
      },
      toCsv: function () {
        var result = [];
        workbook.SheetNames.forEach(function(sheetName) {
          var csv = XLSX.utils.sheet_to_csv(workbook.Sheets[sheetName]);
          if(csv.length > 0){
            result.push('SHEET: ' + sheetName);
            result.push('');
            result.push(csv);
          }
        });
        return result.join("\n");
      },
      toFormulae() {
        var result = [];
        workbook.SheetNames.forEach(function(sheetName) {
          var formulae = XLSX.utils.get_formulae(workbook.Sheets[sheetName]);
          if(formulae.length > 0){
            result.push('SHEET: ' + sheetName);
            result.push('');
            result.push(formulae.join("\n"));
          }
        });
        return result.join("\n");
      }
    };
  };

  ExcelJs.Reader = function (_file, onload) {
    var that = this;

    var file = _file;
    var reader = new FileReader();

    reader.onload = function(e) {
      var data = e.target.result;

      // データが多いとString.fromCharCode()でMaximum call stack size exceededエラーとなるので、
      // 別途関数で処理をする。
      //var arr = String.fromCharCode.apply(null, new Uint8Array(data));
      var arr = handleCodePoints(new Uint8Array(data));

      if (typeof onload == 'function') {
        onload(e, new ExcelJs.File(file, XLSX.read(btoa(arr), {type: 'base64'})));
      }
    };
    reader.readAsArrayBuffer(file);
  };
})(window, window.document);

// see: https://github.com/mathiasbynens/String.fromCodePoint/issues/1
function handleCodePoints(array) {
  var CHUNK_SIZE = 0x8000; // arbitrary number here, not too small, not too big
  var index = 0;
  var length = array.length;
  var result = '';
  var slice;
  while (index < length) {
    slice = array.slice(index, Math.min(index + CHUNK_SIZE, length)); // `Math.min` is not really necessary here I think
    result += String.fromCharCode.apply(null, slice);
    index += CHUNK_SIZE;
  }
  return result;
}

function renderResult(name, content) {
  var elem = document.getElementById('result');
  var html = elem.innerHTML;
  html += '<h3>' + name + '</h3>';
  html += '<pre>' + content + '</pre>';
  elem.innerHTML = html;
}

document.getElementById('import-excel').addEventListener('change', function (evt) {
  var files = evt.target.files;
  var i, f;
  for (i = 0, f = files[i]; i != files.length; ++i) {
    var er = new ExcelJs.Reader(f, function (e, xlsx) {
      renderResult(xlsx.getFile().name, JSON.stringify(xlsx.toJson(), null, 2));
    });
  }
}, false);

ファイルのアップロード用のフォームにxlsxファイルをアップロードすると画面に出力されます。

サンプル作りました。
demo

注意点、疑問点

ファイルのロードタイミングは処理のタイミングとずれる(onloadイベントで通知される)ので、すべてを読み込んで処理させたい場合は、そういう処理を書いてあげる必要があります。
あとJSON形式で取得するときに複数のシートにデータが入っているのに、1シート分のデータしかJSONに変換されないことがあるみたいです。
あまり詳しく調べてないんで、何とも言えませんが、これなんか使えそう。
ローカルでも動くみたいだから、ちょっとした業務効率もできそう。(みたいな本が売ってたような気がする)

一応、Chrome、Firefox、Edgeでは動くみたいです。

tomgoodsun
プログラマやってます。サーバー構築からウェブアプリ作成まで。分類はPHPer属だと思います。
http://www.tom-gs.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away