Webworker を使用して Excel をエクスポートする

Excel ファイルのエクスポートは、プロジェクト開発中に非常に一般的な機能です。Excel ファイルの生成方法は、通常、バックエンド生成とフロントエンド生成に分けられます。

バックエンドが生成した Excel をフロントエンドがダウンロードするには通常 2 つの方法があり、1 つはバックエンドが Excel ファイルの一時ダウンロード アドレスを返し、フロントエンドがそれを直接ダウンロードする方法です。バックエンドは ArrayBuffer バイナリ データを返します。このデータはフロントエンドの処理後にダウンロードされます。

フロントエンドで Excel を生成するのは非常に簡単です。バックエンドは、指定された形式で JSON データを返すだけで済みます。Excel を生成するステップはフロントエンド ブラウザーによって完了されるため、ブラウザーへの負担が大幅に軽減されます。サーバーのリソースを節約します。ただし、フロントエンドがメイン スレッドで大量のデータをエクスポートすると、excel必然的にメイン スレッドがブロックされ、ページがフリーズしてユーザー エクスペリエンスに影響を与えます

SheetJS

XLSXJS とも呼ばれる SheetJS は、公式 Web サイトでは SheetJS と呼ばれています。ブラウザー、nodejs、deno、react-native をサポートしており、ブラウザーは ie10+ と互換性があります。

SheetJS Community Edition は、ほぼすべての複雑なスプレッドシートから有用なデータを抽出し、従来のソフトウェアと最新のソフトウェアで使用できる新しいスプレッドシートを生成するための、実証済みのオープン ソース ソリューションを提供します。--公式サイトより抜粋

十万行 20列 エクスポートされたデータの例を次に示します 。

const cloLen = 20;
const rowLen = 100000;
const row = Array(cloLen).fill().reduce((t, e, i) => ({ ...t, [`字段${i + 1}`]: `字段${i + 1}的值` }), {});
const list = Array(rowLen).fill({ ...row });

const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(list, { dense: true });
XLSX.utils.book_append_sheet(wb, ws);
XLSX.writeFile(wb, `${new Date().toLocaleTimeString()}.xlsx`, {
  bookSST: true,
});

JavaScript はシングルスレッドであるため、多数の JS 操作によりブラウザのレンダリング プロセスがブロックされ、さ​​まざまな程度のブラウザの一時停止アニメーションが発生します。

私の Mac Pro を例にとると、Excel ファイルの生成には約 1700 ~ 1800 ミリ秒かかり十万行 20列、ブラウザも長時間フリーズします。この時間は、行と列の数が増加するにつれて直線的に増加します。

現時点では、ブラウザーのレンダリングのブロックを回避するためにWebWorker を使用する必要があります。

const cloLen = 20;
const rowLen = 100000;
const row = Array(cloLen).fill().reduce((t, e, i) => ({ ...t, [`字段${i + 1}`]: `字段${i + 1}的值` }), {});
const list = Array(rowLen).fill({ ...row });

const SheetJSWebWorker = new Worker(
  URL.createObjectURL(
    new Blob([
      `
      importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
      onmessage = ({ data }) => {
        const wb = XLSX.utils.book_new();
        const ws = XLSX.utils.json_to_sheet(data, { dense: true });
        XLSX.utils.book_append_sheet(wb, ws);
        postMessage(XLSX.write(wb, { type: "array", bookType: "xlsx", bookSST: true, compression: true }))
      };
    `,
    ])
  )
);

SheetJSWebWorker.postMessage(list);
SheetJSWebWorker.onmessage = ({ data }) => {
  const a = document.createElement("a");
  a.download = `${new Date().toLocaleTimeString()}.xlsx`;
  a.href = URL.createObjectURL(
    new Blob([data], { type: "application/octet-stream" })
  );
  a.click();
};

このとき、Excel生成の実行時間は2400~2500ms程度です。ただし、ページはブロックされることなくレンダリングされます。

SheetJS は商用プロジェクトであることに注意してください。完全無料のコミュニティ エディションを使用しています。コミュニティ エディションでは、Excel の生成やセルの結合など、非常に限られた機能のみが提供されます。Excel の生成スタイルをカスタマイズしたい場合は、 xlsx-style や xlsx-populateなどの SheetJS のオープンソース周辺機器を使用する必要があります 。ここでは詳しく説明しませんので、興味のある方はご自身で調べてください。

カスタム関数を使用して Excel をエクスポートする場合は、ExcelJS を使用してそれを実現する必要があります。

ExcelJS

ExcelJS は、より強力なカスタム エクスポート Excel 関数を提供しますが、パフォーマンスは SheetJS の 4 分の 1 にすぎません。

シングルスレッドの例:

const cloLen = 20;
const rowLen = 100000;
const row = Array(cloLen).fill().reduce((t, e, i) => ({ ...t, [`字段${i + 1}`]: `字段${i + 1}的值` }), {});
const list = Array(rowLen).fill({ ...row });

const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet();
worksheet.columns = Object.keys(list[0]).map((e) => ({ header: e, key: e, width: 20 }));
worksheet.addRows(list);

const a = document.createElement("a");
a.download = `${new Date().toLocaleTimeString()}.xlsx`;
a.href = window.URL.createObjectURL(
  new Blob([await workbook.xlsx.writeBuffer()], {
    type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
  })
);
a.click();

マルチスレッドの例:

const cloLen = 20;
const rowLen = 100000;
const row = Array(cloLen).fill().reduce((t, e, i) => ({ ...t, [`字段${i + 1}`]: `字段${i + 1}的值` }), {});
const list = Array(rowLen).fill({ ...row });

const ExcelJSWebWorker = new Worker(
  URL.createObjectURL(
    new Blob([
      `
      importScripts("https://cdn.bootcdn.net/ajax/libs/exceljs/4.3.0/exceljs.js");
      onmessage = async ({ data }) => {
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet();
        worksheet.columns = Object.keys(data[0]).map(e => ({ header: e, key: e, width: 20 }));
        worksheet.addRows(data);
        postMessage(await workbook.xlsx.writeBuffer())
      };
    `,
    ])
  )
);

ExcelJSWebWorker.postMessage(list);
ExcelJSWebWorker.onmessage = ({ data }) => {
  const a = document.createElement("a");
  a.download = `${new Date().toLocaleTimeString()}.xlsx`;
  a.href = window.URL.createObjectURL(
    new Blob([data], {
      type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
    })
  );
  a.click();
};

CSV

ほとんどの場合、エクスポートする Excel ファイルは CSV 形式で生成することもできます。

名前からすると、1つはCSVエクスポート、もう1つはExcelエクスポートです。

それでは、この 2 つの違いは何でしょうか?

Excel はスプレッドシートであり、ファイルを独自の形式である xls または xlsx で保存し、ブック内のすべてのワークシートに関する情報を保持します。

CSV は、カンマ区切り値の略で、一連の値をカンマで区切るプレーン テキスト形式ですが、書式設定、数式、マクロなどが含まれていません。

まとめると、Excelはデータを保存するだけでなく、データに対する演算結果も保存することができ、CSVファイルは単なるテキストファイルであり、データを保存するだけなので、生成は非常に簡単です。

また、CSV文書はWordで開くとデフォルトでExcel形式で表示され、簡単な処理のみでExcelに変換できます。

シングルスレッドの例:

const cloLen = 20;
const rowLen = 100000;
const row = Array(cloLen).fill().reduce((t, e, i) => ({ ...t, [`字段${i + 1}`]: `字段${i + 1}的值` }), {});
const list = Array(rowLen).fill({ ...row });

let str = Object.keys(list[0]).join() + "\n";
for (let i = 0; i < list.length; i++) {
  for (const key in list[i]) {
    str += `${list[i][key] + "\t"},`;
  }
  str += "\n";
}
const a = document.createElement("a");
a.href = "data:text/csv;charset=utf-8,\ufeff" + encodeURIComponent(str);
a.download = `${new Date().toLocaleTimeString()}.csv`;
a.click();

マルチスレッドの例:

const cloLen = 20;
const rowLen = 100000;
const row = Array(cloLen).fill().reduce((t, e, i) => ({ ...t, [`字段${i + 1}`]: `字段${i + 1}的值` }), {});
const list = Array(rowLen).fill({ ...row });

const VanillaJSWebWorker = new Worker(
  URL.createObjectURL(
    new Blob([
      `
      importScripts("https://cdn.bootcdn.net/ajax/libs/exceljs/4.3.0/exceljs.js");
      onmessage = async ({ data }) => {
        let str = Object.keys(data[0]).join() + String.fromCharCode(10)
        for (let i = 0; i < data.length; i++) {
          for (const key in data[i]) {
            str += data[i][key] + '\t,';
          }
          str += String.fromCharCode(10);
        }
        postMessage('data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str))
      };
    `,
    ])
  )
);

VanillaJSWebWorker.postMessage(list);
VanillaJSWebWorker.onmessage = ({ data }) => {
  const a = document.createElement("a");
  a.download = `${new Date().toLocaleTimeString()}.csv`;
  a.href = data;
  a.click();
};

要約する

フロントエンドで Excel を生成する最大の利点は、サーバー リソースの消費を削減し、クライアントの計算能力リソースを最大限に活用し、計算圧力をサーバーからブラウザーに転送できることです。送信されるデータ量は少なく、比較的高速です。

同時に、欠点も明らかであり、まず、JavaScript は高いコンピューティング性能を備えたプログラミング言語ではなく、その相対的なコンピューティング性能はサーバーコンピューティングほど良くありません。次に、エクスポート ファイルの生成速度に影響を与える最も重要な要素はユーザーのハードウェアに依存し、コンピューターのパフォーマンスが異なるとユーザー エクスペリエンスに大きな違いが生じる可能性があります。

以下は、上記のソリューションに適用できる一般的なシナリオです。以下を参照してください。

  • 数百万のデータが CSV を使用しています。
  • データ量の多いスタイルのない機能には SheetJS が必要です。
  • 少量のデータは ExcelJS を使用して完全に機能します。
  • 大量のデータには、SheetJS の商用バージョンを使用する機能が必要です。 

関連ドキュメントのリンク

 

おすすめ

転載: blog.csdn.net/qq_44376306/article/details/131271612