如何使用JavaScript实现纯前端读取和导出excel文件
**转载自 小茗同学 的 如何使用JavaScript实现纯前端读取和导出excel文件
js-xlsx 介绍
由
SheetJS
出品的
js-xlsx
是一款非常方便的只需要纯JS即可读取和导出excel的工具库,功能强大,支持格式众多,支持
xls
、
xlsx
、
ods
(一种OpenOffice专有表格文件格式)等十几种格式。本文全部都是以
xlsx
格式为例。
官方github:
https://github.com/SheetJS/js-xlsx
本文配套demo在线演示地址:
http://demo.haoji.me/2017/02/08-js-xlsx/
1.1. 兼容性
兼容性如下图:
1.2. 如何使用
dist
目录下有很多个JS文件,一般情况下用
xlsx.core.min.js
就够了,
xlsx.full.min.js
则是包含了所有功能模块。
直接
script
标签引入即可:
<script type="text/javascript" src="./js/xlsx.core.min.js"></script> 复制运行
读取excel
读取excel主要是通过
XLSX.read(data, {type: type});
方法来实现,返回一个叫
WorkBook
的对象,type主要取值如下:
-
base64
: 以base64方式读取; -
binary
: BinaryString格式(byte n is data.charCodeAt(n)) -
string
: UTF8编码的字符串; -
buffer
: nodejs Buffer; -
array
: Uint8Array,8位无符号数组; -
file
: 文件的路径(仅nodejs下支持);
2.1. 获取workbook对象
2.1.1. 读取本地文件
直接上代码:
// 读取本地excel文件
function readWorkbookFromLocalFile(file, callback) {
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
var workbook = XLSX.read(data, {type: 'binary'});
if(callback) callback(workbook);
};
reader.readAsBinaryString(file);
}
// 读取本地excel文件
function readWorkbookFromLocalFile(file, callback) {
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
var workbook = XLSX.read(data, {type: 'binary'});
if(callback) callback(workbook);
};
reader.readAsBinaryString(file);
}
复制运行
2.1.2. 读取网络文件
// 从网络上读取某个excel文件,url必须同域,否则报错
function readWorkbookFromRemoteFile(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if(xhr.status == 200) {
var data = new Uint8Array(xhr.response)
var workbook = XLSX.read(data, {type: 'array'});
if(callback) callback(workbook);
}
};
xhr.send();
}
// 从网络上读取某个excel文件,url必须同域,否则报错
function readWorkbookFromRemoteFile(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if(xhr.status == 200) {
var data = new Uint8Array(xhr.response)
var workbook = XLSX.read(data, {type: 'array'});
if(callback) callback(workbook);
}
};
xhr.send();
}
复制运行
2.2. 详解 workbook
2.2.1. Workbook Object
workbook
里面有什么东西呢,我们打印出来看一下:
可以看到,
SheetNames
里面保存了所有的sheet名字,然后
Sheets
则保存了每个sheet的具体内容(我们称之为
Sheet Object
)。每一个
sheet
是通过类似
A1
这样的键值保存每个单元格的内容,我们称之为单元格对象(
Cell Object
):
2.2.2. Sheet Object
每一个
Sheet Object
表示一张表格,只要不是
!
开头的都表示普通
cell
,否则,表示一些特殊含义,具体如下:
-
sheet['!ref']
:表示所有单元格的范围,例如从A1到F8则记录为A1:F8
; -
sheet[!merges]
:存放一些单元格合并信息,是一个数组,每个数组由包含s
和e
构成的对象组成,s
表示开始,e
表示结束,r
表示行,c
表示列; - 等等;
关于单元格合并,看懂下面这张图基本上就没问题了:
结果如下:
2.2.3. 单元格对象
每一个单元格是一个对象(
Cell Object
),主要有
t
、
v
、
r
、
h
、
w
等字段(详见
这里
):
-
t:表示内容类型,
s
indicates the string type.n
denotes the number type.b
denotes the boolean type.d
for date type, etc. - v: indicates the original value.
-
f: denotes a formula, such as
B2+B3
; the - h: HTML content
- w: formatted content
-
r: rich text content
rich text
- etc
2.2.4. Reading the workbook
Common method.
// Read an excel file
function outputWorkbook(workbook) {
var sheetNames = workbook.SheetNames; // collection of worksheet names
sheetNames.forEach(name => {
var worksheet = workbook.Sheets[name]; // Only worksheet names can be used to get the specified worksheet
for(var key in worksheet) {
// v is reading the original value of the cell
console.log(key, key[0] === '!' ? worksheet[key] : worksheet[key].v);
}
});
}
// Read the excel file
function outputWorkbook(workbook) {
var sheetNames = workbook.SheetNames; // collection of worksheet names
sheetNames.forEach(name => {
var worksheet = workbook.Sheets[name]; // Only worksheet names can be used to get the specified worksheet
for(var key in worksheet) {
// v is reading the original value of the cell
console.log(key, key[0] === '!' ? worksheet[key] : worksheet[key].v);
}
});
}
Copy and run
Based on the
!ref
Determine the range of the excel and then the range of the
!merges
Determine the cell merge (if any) and finally output the entire table, which is a bit tricky, but luckily the plugin itself has already written the tool class
XLSX.utils
for us to use directly, without having to traverse it ourselves. The tool class output consists mainly of the following.
Some are not commonly used, the main ones that are commonly used are
-
XLSX.utils.sheet_to_csv
: Generate CSV format -
XLSX.utils.sheet_to_txt
: Generate plain text format -
XLSX.utils.sheet_to_html
: Generating HTML format -
XLSX.utils.sheet_to_json
: Output in JSON format
The main ones commonly used are
sheet_to_csv
or
sheet_to_html
If you convert to csv, the formatting and cell merge information will be ignored, so complex tables may not work. If you convert to html, the cell merge is preserved, but the generated
<html></html>
の代わりにコード
<table></table>
テーブルのカスタマイズをするのはそれほど簡単ではないので、状況に応じたツールクラスの使い分けが必要です。
csv変換方式で結果を出力する簡単な例は、こちらで公開されています。 オンラインデモ こちら
function readWorkbook(workbook)
{
var sheetNames = workbook.SheetNames; // collection of sheet names
var worksheet = workbook.Sheets[sheetNames[0]]; // Here we only read the first sheet
var csv = XLSX.utils.sheet_to_csv(worksheet);
document.getElementById('result').innerHTML = csv2table(csv);
}
// csv を単純なテーブルに変換すると、セルの結合は無視され、最初の行と列に Excel のようなインデックスが追加されます。
機能
csv2table
(
csv
)
{
<未定義
ヴァル
html
=
<スパン
'<table>'
<スパン
;
ヴァル
列
=
csv
.
スプリット
<スパン
(
'\n'
)
;
列
.
ポップ
<スパン
(
<スパン
)
;
// 最後の行は無駄です
列
.
forEach
(
機能
(
行
,
IDX
)
<スパン
{
<未定義
ヴァル
列
=
行
.
スプリット
<スパン
(
','
)
;
コラム
.
アンシフト
(
IDX
+
1
)
;
// 行インデックスを追加する
<スパン
もし
(
idx
<マーク
0
)
{
<未定義
// カラムインデックスの追加
html
+
=
<スパン
'<tr>'
<スパン
;
について
(
ヴァル
i
=
<スパン
0
;
i
<スパン
<
列
.
長さ
;
i
++
)
{
<未定義
html
+
=
<スパン
'<th>'
<スパン
+
(
i
0
?
''
:
文字列
<スパン
.
fromCharCode
(
<スパン
65
<スパン
+
i
-1
)
<スパン
)
<スパン
+
'</th>' です。
;
}
html
+
=
<スパン
'</tr>' とします。
<スパン
;
}
html
+
=
<スパン
'<tr>'
<スパン
;
コラム
.
forEach
(
機能
(
列
)
{
<未定義
html
+
=
'<td>' です。
+
列
+
'</td>' とします。
;
}
)
;
html
+
=
<スパン
'</tr>' とします。
<スパン
;
}
)
;
html
+
=
<スパン
'</table>' とします。
<スパン
;
戻る
html
;
}
関数 readWorkbook(workbook)
{
<未定義
var sheetNames = workbook.SheetNames; // シート名のコレクション
Sheets[sheetNames[0]]; // ここでは、最初のシートのみを読み込む。
var csv = XLSX.utils.sheet_to_csv(worksheet).XLSX.utils.sheet_to_csv(worksheet);
document.getElementById('result').innerHTML = csv2table(csv);
}
// csv を単純なテーブルに変換すると、セルの結合は無視され、最初の行と列に Excel のようなインデックスが追加されます。
関数 csv2table(csv)
{
<未定義
var html = '<table>';
var rows = csv.split('\n');
rows.pop(); // 最後の行は無駄です。
rows.forEach(function(row, idx) {)
<未定義
var columns = row.split(',');
columns.unshift(idx+1); // 行のインデックスを追加する
if(idx == 0) { // 列のインデックスを追加する。
html += '<tr>';
for(var i=0; i<columns.length;i++){。
<未定義
html += '<th>' + (i==0?':String.fromCharCode(65+i-1)) + '</th> ';
}
html += '</tr>';
}
html += '<tr>';
columns.forEach(function(column) {)
<未定義
html += '<td>'+column+'</td>';
});
html += '</tr>';
});
html += '</table>';
htmlを返します。
}
コピー
<スパン
実行
エクセルの書き出し
エクスポートには、既存のエクセルを修正したものをベースにするものと、新たに生成するものの2種類がありますが、前者の方が簡単なので、ここでは後者に焦点を当てます。
3.1. 手書きコードの生成
エクセルファイルの書き出し、主に生成方法について
sheet
ここでは、最もシンプルなcsv to excelの例を書きます。
// csv to sheet object
function csv2sheet(csv) {
var sheet = {}; // The sheet to be generated
csv = csv.split('\n');
csv.forEach(function(row, i) {
row = row.split(',');
if(i == 0) sheet['!ref'] = 'A1:'+String.fromCharCode(65+row.length-1)+(csv.length-1);
row.forEach(function(col, j) {
sheet[String.fromCharCode(65+j)+(i+1)] = {v: col};
});
});
return sheet;
}
// 最終的なエクセルファイルのためにシートをblobオブジェクトに変換し、URL.createObjectURLを使用してダウンロードします。
機能
シート2ブロブ
<スパン
(
シート
,
シート名
)
{
<未定義
シート名
=
シート名
||
'シート1'
;
ヴァル
ワークブック
=
<スパン
{
<未定義
シート名
:
[
シート名
]
,
シーツ
:
{
<未定義
}
}
;
ワークブック
.
シーツ
[
シート名
]
=
シート
;
// エクセル用の設定項目を生成する
ヴァル
ウォプテックス
=
<スパン
{
<未定義
ブックタイプ
:
'xlsx'
,
// 生成するファイルの種類
ブックSST
:
虚偽
,
// 共有文字列テーブルを生成するかどうか、公式の説明によると、生成する場合は速度が遅くなりますが、下位のIOSデバイスでの互換性が向上します。
タイプ
:
'バイナリ'
}
;
ヴァル
アウトゥブ
=
エックスエルエスエックス
.
書く
(
ワークブック
,
ふせじ
)
;
ヴァル
ブロッブ
=
新
ブロブ
(
[
ツーカーブ
(
アウトゥブ
)
]
,
{
<未定義
タイプ
:
application/octet-stream"
}
)
;
// 文字列を ArrayBuffer に変換
機能
ツーカーブ
(
s
)
{
<未定義
ヴァル
バフ
=
新
ArrayBuffer
(
s
.
長さ
)
;
ヴァル
見晴らし
=
新
Uint8Array
(
バフ
)
;
にとって
(
ヴァル
i
=
0
;
i
!
=
s
.
長さ
;
++
i
)
ビュー
<スパン
[
i
]
=
s
.
charCodeAt
<スパン
(
i
)
&です。
0xFF
;
戻る
buf
;
}
戻る
ブロブ
;
}
// csv をシートオブジェクトに変換
関数 csv2sheet(csv) {
<未定義
var sheet = {}; // 生成されるシート
csv = csv.split('\n');
csv.forEach(function(row, i) {)
<未定義
row = row.split(',');
if(i == 0) sheet['!ref'] = 'A1:'+String.fromCharCode(65+row.length-1)+(csv.length-1);
row.forEach(function(col, j) {)
<未定義
sheet[String.fromCharCode(65+j)+(i+1)] = {v: col};
});
});
を返します。
}
// 最終的なエクセルファイルのためにシートをblobオブジェクトに変換し、URL.createObjectURLを使用してダウンロードします。
関数 sheet2blob(sheet, sheetName) { [シート, シート名
<未定義
sheetName = sheetName || 'sheet1';
var workbook = {
<未定義
SheetNames。[シート名],
シートです。{}
};
workbook.Sheets[sheetName] = sheet;
// エクセル用の設定項目を生成する
var wopts = {
<未定義
bookType: 'xlsx', // 生成するファイルの種類
bookSST: false, // Shared String Tableを生成するかどうか、公式の説明では生成すると速度が落ちるが、バージョンの低いIOSデバイスでは互換性があるとのこと
タイプ: 'バイナリ'
};
var wbout = XLSX.write(workbook, wopts);
var blob = new Blob([s2ab(wbout)], {type: "application/octet-stream"});
// 文字列をArrayBufferに変換
関数 s2ab(s) {
<未定義
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
bufを返します。
}
blobを返します。
}
コピー
<スパン
実行
上記のblobオブジェクトを取得し、それを直接ダウンロードすることができます、私の以前の投稿を参照してください。
JSポップアップダウンロードダイアログ
をラップしています。
openDownloadDialog
メソッドを使用します。
/**
* Generic open download dialog method, not tested for specific compatibility
* @param url download address, can also be a blob object, mandatory
* @param saveName Save file name, optional
*/
function openDownloadDialog(url, saveName)
{
if(typeof url == 'object' && url instanceof Blob)
{
url = URL.createObjectURL(url); // create blob address
}
var aLink = document.createElement('a');
aLink.href = url;
aLink.download = saveName || ''; // HTML5 new attribute, specify save file name, can not be suffixed, note, file:/// mode will not take effect
var event;
if(window.MouseEvent) event = new MouseEvent('click');
else
{
event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
}
aLink.dispatchEvent(event);
}
/**
* Generic open download dialog method, not tested for specific compatibility
* @param url download address, can also be a blob object, mandatory
* @param saveName Save file name, optional
*/
function openDownloadDialog(url, saveName)
{
if(typeof url == 'object' && url instanceof Blob)
{
url = URL.createObjectURL(url); // create blob address
}
var aLink = document.createElement('a');
aLink.href = url;
aLink.download = saveName || ''; // HTML5 new attribute, specify save file name, can not be suffixed, note, file:/// mode will not take effect
var event;
if(window.MouseEvent) event = new MouseEvent('click');
else
{
event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
}
aLink.dispatchEvent(event);
}
Copy and run
ということで、最終的なダウンロードの実装は以下のようになります。
// Pass in the csv and the download box will pop up after execution
function exportExcel(csv) {
var sheet = csv2sheet(csv);
var blob = sheet2blob(sheet);
openDownloadDialog(blob, 'export.xlsx').
}
// Pass in csv and the download box will pop up after execution
function exportExcel(csv) {
var sheet = csv2sheet(csv);
var blob = sheet2blob(sheet);
openDownloadDialog(blob, 'export.xlsx').
}
Copy and run
3.2. 公式ツールクラスを使って生成
上記のコードを手で書く必要はありません。すでに公式の既成のツールクラスがあり、主に以下のようなものが使えます。
-
aoa_to_sheet
: このツールクラスは最も強力で便利なもので、2次元配列をシートに変換し、数値、文字列、ブーリアン、日付などの型のデータを自動的に処理します。 -
table_to_sheet
: の名前です。table dom
を直接シートに書き込むと、そのシートは自動的にcolspan
とrowspan
を作成し、対応するセルにマージします。 -
json_to_sheet
: オブジェクトの配列をシートに変換します。
aoa_to_sheet
例
var aoa = [
['Name', 'Gender', 'Age', 'Registration Time'],
['Zhang San', 'Male', 18, new Date()],
['Li Si', 'F', 22, new Date()], ['Li Si', 'F', 22, new Date()]
];
var sheet = XLSX.utils.aoa_to_sheet(aoa);
openDownloadDialog(sheet2blob(sheet), 'export.xlsx');
var aoa = [
['Name', 'Gender', 'Age', 'Registration Time'],
['Zhang San', 'Male', 18, new Date()],
['Li Si', 'F', 22, new Date()], ['Li Si', 'F', 22, new Date()]
];
var sheet = XLSX.utils.aoa_to_sheet(aoa);
openDownloadDialog(sheet2blob(sheet), 'export.xlsx');
Copy and run
table_to_sheet
さらにシンプルに
XLSX.utils.table_to_sheet($('table')[0])
で終わりです。
3.3. セル結合の処理
一般に、フロントエンドでは
excel
の代わりに
csv
主な目的は、csvがセルを結合できない問題を解決することです。そうでなければ、csvファイルを直接エクスポートする方が良いのに、なぜ何百キロバイトものプラグインを導入するのでしょうか。
例えば、次のような形式のExcelファイルを作成したいとします。
A1-C1
セルの結合を行うこと。
コードは以下の通りです。
var aoa = [
['Main Info', null, null, 'Other Info'], // pay special attention to the merge where 2 null are left after
['name', 'gender', 'age', 'registration time'],
['Zhang San', 'Male', 18, new Date()],
['Li Si', 'F', 22, new Date()], ['Li Si', 'F', 22, new Date()]
];
var sheet = XLSX.utils.aoa_to_sheet(aoa);
sheet['!merges'] = [
// set the cell merge A1-C1
{s: {r: 0, c: 0}, e: {r: 0, c: 2}}
];
openDownloadDialog(sheet2blob(sheet), 'cell merge example.xlsx');
var aoa = [
['Main Info', null, null, 'Other Info'], // pay special attention to the merge where 2 nulls are left after
['name', 'gender', 'age', 'registration time'],
['Zhang San', 'Male', 18, new Date()],
['Li Si', 'F', 22, new Date()], ['Li Si', 'F', 22, new Date()]
];
var sheet = XLSX.utils.aoa_to_sheet(aoa);
sheet['!merges'] = [
// set the cell merge A1-C1
{s: {r: 0, c: 0}, e: {r: 0, c: 2}}
];
openDownloadDialog(sheet2blob(sheet), 'cell merge example.xlsx');
Copy and run
重要なことは、マージされるセルは、マージされるセルが
null
でなければ、後ろのコンテンツ(この場合、4列目)の
Additional information
)は上書きされます。
3.4. カスタムスタイル
通常版では、フォント、色、背景色などの定義には対応していません。この機能が必要な場合は プロ版 ホームページにはダウンロードのアドレスがないので、お金がかかるようです。
関連
-
[解決済み】 Uncaught TypeError: data.push is not a function
-
[解決済み】Javascript:getElementById対getElementsById(両方が別のページで動作する)。
-
[解決済み】Angular JS Uncaught Error。[インジェクター:モジュラー]。
-
[解決済み] Webアプリでfacebook共有を実装する際にエラーが発生する パラメータ app_id is required
-
[解決済み] 一度の置換呼び出しで複数の文字を置換する
-
[解決済み] JavaScriptのオブジェクトを破棄するには?
-
[解決済み] Uncaught ReferenceError: importScripts が定義されていません。
-
[解決済み] 変数宣言と変数の束縛は同じですか?
-
[解決済み] javascriptでjsonの日付をdd/mm/yyの形式でフォーマットするにはどうすればよいですか?
-
[解決済み] document.querySelector(...) is null エラー
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Syntax error: JavaScriptの不正なreturnステートメント
-
[解決済み] WebStormのエラー:式文が代入または呼び出しでない
-
[解決済み] TypeError: document.getElementbyId is not a function [closed].
-
[解決済み] tinyhipposが注入されたスクリプトの目的は何ですか?
-
[解決済み] _references.jsは何に使うのですか?
-
[解決済み] contenteditable変更イベント
-
[解決済み] Google MapsのAPIを使用して地図を作成する際の問題点
-
[解決済み] JavaScriptによる自動投稿フォーム
-
[解決済み] VEndを用いた入力規則と要求事項
-
[解決済み] dojo.byIdとdijit.byIdの違いは何ですか?