Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
xlsx — SheetJS xlsx 0.18.5 fork with CVE-2023-30533 and CVE-2024-22363 fixes | Kitploit
工具/GitHubGitHub/weareu/xlsx
General Purpose UtilitiesEncryption/Decryption ToolsData RecoveryUtilities & Frameworks
GitHubweareu/xlsx

xlsx

SheetJS xlsx 0.18.5 fork with CVE-2023-30533 and CVE-2024-22363 fixes

查看仓库
6个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

SheetJS

SheetJS 社区版提供了经过实战检验的开源解决方案,用于从几乎任何复杂的电子表格中提取有用数据,并生成能够兼容旧版与现代软件的新电子表格。

SheetJS Pro 提供的解决方案不仅限于数据处理:轻松编辑复杂模板;发挥您的创意进行样式设计;制作包含图片/图表/数据透视表的自定义工作表;评估公式表达式并将计算移植到 Web 应用;自动化常见电子表格任务,以及更多功能!

License Build Status Snyk Vulnerabilities npm Downloads Analytics

浏览器测试与支持矩阵

Build Status

支持的文件格式

circo graph of format support

graph legend

目录

展开查看目录
  • 开始使用
    • 安装
    • 使用方法
    • SheetJS 的禅意
    • JS 生态系统演示
  • 获取与提取数据
    • 解析工作簿
    • 处理 JSON 和 JS 数据
    • 处理 HTML 表格
  • 处理数据
    • 修改工作簿结构
    • 修改单元格值
    • 修改其他工作表/工作簿/单元格属性
  • 打包与释放数据
    • 写入工作簿
    • 写入示例
    • 流式写入
    • 生成 JSON 和 JS 数据
    • 生成 HTML 表格
    • 生成单个工作表快照
  • 接口
    • 解析函数
    • 写入函数
    • 工具函数
  • 通用电子表格格式
    • 通用结构
    • 单元格对象
      • 数据类型
      • 日期
    • 工作表对象
      • 工作表对象
      • 图表工作表对象
      • 宏工作表对象
      • 对话框工作表对象
    • 工作簿对象
      • 工作簿文件属性
    • 工作簿级属性
      • 已定义的名称
      • 工作簿视图
      • 杂项工作簿属性
    • 文档特性
      • 公式

开始使用

安装

独立浏览器脚本

完整的浏览器独立构建版本保存为 dist/xlsx.full.min.js,可以直接通过 script 标签添加到页面中:```html

root@kitploit:~
<details>
  <summary><b>CDN 可用性</b>(点击显示)</summary>

|    CDN     | URL                                        |
|-----------:|:-------------------------------------------|
|    `unpkg` | <https://unpkg.com/xlsx/>                  |
| `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx>    |
|    `CDNjs` | <https://cdnjs.com/libraries/xlsx>         |

例如,`unpkg` 将最新版本提供在:```html
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
浏览器构建 (点击展开)

完整单文件版本生成于 dist/xlsx.full.min.js

dist/xlsx.core.min.js 省略了代码页库(不支持 XLS 编码)

更精简的构建版本生成于 dist/xlsx.mini.min.js。与完整构建相比:

  • 省略了代码页库(不支持 XLS 编码)
  • 不支持 XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Numbers
  • 移除了 node 流工具

使用 bower:```bash $ bower install js-xlsx

root@kitploit:~
**ECMAScript 模块**

ECMAScript 模块构建保存为 `xlsx.mjs`,可以直接通过使用 `type=module` 的 `script` 标签添加到页面中:```html
<script type="module">
import { read, writeFileXLSX } from "./xlsx.mjs";

/* load the codepage support library for extended support with older formats  */
import { set_cptable } from "./xlsx.mjs";
import * as cptable from './dist/cpexcel.full.mjs';
set_cptable(cptable);
</script>

npm 包也通过 module 参数暴露模块,在 Angular 和其他项目中被支持:```ts import { read, writeFileXLSX } from "xlsx";

/* load the codepage support library for extended support with older formats */ import { set_cptable } from "xlsx"; import * as cptable from 'xlsx/dist/cpexcel.full.mjs'; set_cptable(cptable);

root@kitploit:~
**Deno**

`xlsx.mjs` 可以在 Deno 中导入。它可以从 `unpkg` 获取:```ts
// @deno-types="https://unpkg.com/xlsx/types/index.d.ts"
import * as XLSX from 'https://unpkg.com/xlsx/xlsx.mjs';

/* load the codepage support library for extended support with older formats  */
import * as cptable from 'https://unpkg.com/xlsx/dist/cpexcel.full.mjs';
XLSX.set_cptable(cptable);

NodeJS

使用 npm:```bash $ npm install xlsx

root@kitploit:~
默认情况下,该模块支持 `require`:```js
var XLSX = require("xlsx");

该模块还附带 xlsx.mjs,用于与 import 一起使用:```js import * as XLSX from 'xlsx/xlsx.mjs';

/* load 'fs' for readFile and writeFile support */ import * as fs from 'fs'; XLSX.set_fs(fs);

/* load 'stream' for stream support */ import { Readable } from 'stream'; XLSX.stream.set_readable(Readable);

/* load the codepage support library for extended support with older formats */ import * as cpexcel from 'xlsx/dist/cpexcel.full.mjs'; XLSX.set_cptable(cpexcel);

root@kitploit:~
**Photoshop 和 InDesign**

`dist/xlsx.extendscript.js` 是面向 Photoshop 和 InDesign 的 ExtendScript 构建版本,
包含在 `npm` 包中。可以通过 `#include` 指令直接引用:```extendscript
#include "xlsx.extendscript.js"
Internet Explorer 与 ECMAScript 3 兼容性 (点击展开)

为了与JavaScript引擎广泛兼容,该库使用ECMAScript 3语言方言以及一些ES5特性(如Array#forEach)编写。旧版浏览器需要shim来提供缺失的函数。

要使用shim,请在加载xlsx.js的脚本标签之前添加shim:```html

root@kitploit:~
该脚本还包含了 `IE_LoadFile` 和 `IE_SaveFile`,用于在 Internet Explorer 6-9 中加载和保存文件。`xlsx.extendscript.js` 脚本以适合 Photoshop 和其他 Adobe 产品的格式捆绑了该垫片。

</details>

### 用法

大多数涉及电子表格和数据的场景可以分解为 5 个部分:

1) **获取数据**:数据可以存储在任何地方:本地或远程文件、数据库、HTML TABLE,甚至可以在网页浏览器中通过编程生成。

2) **提取数据**:对于电子表格文件,这涉及解析原始字节以读取单元格数据。对于一般的 JS 数据,这涉及重塑数据。

3) **处理数据**:从生成汇总统计到清理数据记录,这一步是问题的核心。

4) **打包数据**:这可以包括制作新的电子表格、使用 `JSON.stringify` 序列化、编写 XML,或者仅仅为 UI 工具展平数据。

5) **发布数据**:电子表格文件可以上传到服务器或本地写入。数据可以通过 HTML TABLE 或数据网格呈现给用户。

一个常见的问题是,如何从存储在 HTML 表格中的数据生成有效的电子表格导出。在此示例中,将抓取页面上的 HTML TABLE,在底部添加包含报告日期的行,然后生成一个新文件并尝试本地下载。`XLSX.writeFile` 负责打包数据并尝试本地下载:```js
// Acquire Data (reference to the HTML table)
var table_elt = document.getElementById("my-table-id");

// Extract Data (create a workbook object from the table)
var workbook = XLSX.utils.table_to_book(table_elt);

// Process Data (add a new row)
var ws = workbook.Sheets["Sheet1"];
XLSX.utils.sheet_add_aoa(ws, [["Created "+new Date().toISOString()]], {origin:-1});

// Package and Release Data (`writeFile` tries to write and save an XLSB file)
XLSX.writeFile(workbook, "Report.xlsb");

此库试图通过函数简化步骤2和4:从电子表格文件中提取有用数据(read / ),以及根据数据生成新的电子表格文件( / )。其他实用函数如可与HTML表格等常见数据源配合使用。

下载工具
  • 行与列属性
  • 数字格式
  • 超链接
  • 单元格注释
  • 工作表可见性
  • VBA 和宏
  • 解析选项
    • 输入类型
    • 猜测文件类型
  • 写入选项
    • 支持的输出格式
    • 输出类型
  • 工具函数
    • 数组的数组输入
    • 对象数组输入
    • HTML 表格输入
    • 公式输出
    • 分隔符分隔的输出
      • UTF-16 Unicode 文本
    • HTML 输出
    • JSON
  • 文件格式
  • 测试
    • Node
    • 浏览器
    • 测试环境
    • 测试文件
  • 贡献指南
    • OSX/Linux
    • Windows
    • 测试
  • 许可证
  • 参考资料
  • readFile
    write
    writeFile
    table_to_book

    本文档及多个演示项目涵盖了步骤1和5的多种常见场景与方法。

    实用函数有助于步骤3。

    "获取与提取数据" 描述了常见数据导入场景的解决方案。

    "打包与发布数据" 描述了常见数据导出场景的解决方案。

    "处理数据" 描述了常见工作簿处理与操作场景的解决方案。

    "实用函数" 详细介绍了用于将JSON数组及其他常见JS结构转换为工作表对象的实用函数。

    SheetJS 的禅意

    数据处理应适配任何工作流

    该库并不强加独立生命周期。它能很好地融入任何框架构建的网站和应用程序中。纯JS数据对象与Web Workers及未来API也能良好配合。

    JavaScript 是一种强大的数据处理语言

    "通用电子表格格式" 是对工作簿核心概念的简单对象表示。库中的各种函数提供了操作该对象的底层工具。

    为方便JS处理,提供了将工作表部分内容与数组的数组相互转换的实用函数。以下示例结合了强大的JS数组方法与网络请求库,用于下载数据、选择所需信息并创建工作簿文件:

    从JSON端点获取数据并生成工作簿(点击显示)

    目标是生成一个包含美国总统姓名和生日的XLSB工作簿。

    获取数据

    原始数据

    https://theunitedstates.io/congress-legislators/executive.json 包含所需数据。例如,约翰·亚当斯:```js { "id": { /* (data omitted) / }, "name": { "first": "John", // <-- first name "last": "Adams" // <-- last name }, "bio": { "birthday": "1735-10-19", // <-- birthday "gender": "M" }, "terms": [ { "type": "viceprez", / (other fields omitted) / }, { "type": "viceprez", / (other fields omitted) / }, { "type": "prez", / (other fields omitted) */ } // <-- look for "prez" ] }

    root@kitploit:~
    _Filtering for Presidents_
    
    数据集中包括阿龙·伯尔,一位从未担任过总统的副总统!
    
    `Array#filter` 创建一个包含所需行的新数组。总统至少担任过一届 `type` 为 `"prez"` 的任期。
    要测试某行是否至少有一项 `"prez"` 任期,`Array#some` 是另一个原生 JS 函数。
    完整的过滤器如下:```js
    const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
    

    整理数据

    在此示例中,姓名将由名字和姓氏组合而成(row.name.first + " " + row.name.last),而生日将是子字段 row.bio.birthday。使用 Array#map,可以在一次调用中处理数据集:```js const rows = prez.map(row => ({ name: row.name.first + " " + row.name.last, birthday: row.bio.birthday }));

    root@kitploit:~
    结果是一个由"简单"对象组成的数组,没有嵌套:```js
    [
      { name: "George Washington", birthday: "1732-02-22" },
      { name: "John Adams", birthday: "1735-10-19" },
      // ... one row per President
    ]
    

    提取数据

    借助清洗后的数据集,XLSX.utils.json_to_sheet 生成一个工作表:```js const worksheet = XLSX.utils.json_to_sheet(rows);

    root@kitploit:~
    `XLSX.utils.book_new` 创建一个新工作簿,`XLSX.utils.book_append_sheet` 将工作表追加到工作簿。新的工作表将命名为"Dates":```js
    const workbook = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
    

    处理数据

    修正表头

    默认情况下,json_to_sheet 会创建一个带有标题行的工作表。在此情况下, 标题来自 JS 对象键:"name" 和 "birthday"。

    标题位于单元格 A1 和 B1。XLSX.utils.sheet_add_aoa 可以向现有工作表中从单元格 A1 开始写入文本值:```js XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

    root@kitploit:~
    _Fixing Column Widths_
    
    Some of the names are longer than the default column width.  Column widths are
    set by [setting the `"!cols"` worksheet property](#row-and-column-properties).
    
    The following line sets the width of column A to approximately 10 characters:```js
    worksheet["!cols"] = [ { wch: 10 } ]; // set column A width to 10 characters
    

    对 rows 执行一次 Array#reduce 调用可以计算出最大宽度:```js const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10); worksheet["!cols"] = [ { wch: max_width } ];

    root@kitploit:~
    注意:如果起始点是一个文件或HTML表格,`XLSX.utils.sheet_to_json` 将生成一个由JS对象组成的数组。
    
    **包与发布数据**
    
    `XLSX.writeFile` 创建一个电子表格文件并尝试将其写入系统。在浏览器中,它将尝试提示用户下载文件。在NodeJS中,它将写入本地目录。```js
    XLSX.writeFile(workbook, "Presidents.xlsx");
    

    完整示例```js // Uncomment the next line for use in NodeJS: // const XLSX = require("xlsx"), axios = require("axios");

    (async() => { /* fetch JSON data and parse */ const url = "https://theunitedstates.io/congress-legislators/executive.json"; const raw_data = (await axios(url, {responseType: "json"})).data;

    /* filter for the Presidents */ const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

    /* flatten objects */ const rows = prez.map(row => ({ name: row.name.first + " " + row.name.last, birthday: row.bio.birthday }));

    /* generate worksheet and workbook */ const worksheet = XLSX.utils.json_to_sheet(rows); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");

    /* fix headers */ XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

    /* calculate column width */ const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10); worksheet["!cols"] = [ { wch: max_width } ];

    /* create an XLSX file and try to save to Presidents.xlsx */ XLSX.writeFile(workbook, "Presidents.xlsx"); })();

    root@kitploit:~
    为了在网页浏览器中使用,假设代码片段保存为 `snippet.js`,应使用 script 标签来包含 `axios` 和 `xlsx` 的独立构建版本:```html
    <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="snippet.js"></script>
    

    文件格式属于实现细节

    解析器覆盖了多种常见的电子表格文件格式,以确保“HTML另存为XLS”文件以及实际的XLS或XLSX文件都能正常工作。

    写入器支持多种常见输出格式,以与数据生态系统广泛兼容。

    在最大程度上,数据处理代码无需担心所涉及的具体文件格式。

    JS生态系统示例

    demos目录 包含以下示例项目:

    框架和API

    • angularjs
    • angular and ionic
    • knockout
    • meteor
    • react and react-native
    • vue 2.x and weex
    • XMLHttpRequest and fetch
    • nodejs server
    • databases and key/value stores
    • typed arrays and math

    打包工具和工具链

    • browserify
    • fusebox
    • parcel
    • requirejs
    • rollup
    • systemjs
    • typescript
    • webpack 2.x

    平台与集成

    • deno
    • electron application
    • nw.js application
    • Chrome / Chromium extensions
    • Download a Google Sheet locally
    • Adobe ExtendScript
    • Headless Browsers
    • canvas-datagrid
    • x-spreadsheet
    • react-data-grid
    • vue3-table-light
    • Swift JSC and other engines
    • "serverless" functions
    • internet explorer

    其他示例包含在showcase中。

    https://sheetjs.com/demos/modify.html 展示了读取、修改和写入文件的完整示例。

    https://github.com/SheetJS/sheetjs/blob/HEAD/bin/xlsx.njs 是Node安装中包含的命令行工具,用于读取电子表格文件并以多种格式导出内容。

    获取和提取数据

    解析工作簿

    API

    从电子表格字节中提取数据```js var workbook = XLSX.read(data, opts);

    root@kitploit:~
    `read` 方法可以从存储在 JS 字符串、"binary string"、NodeJS 缓冲区或类型化数组(`Uint8Array` 或 `ArrayBuffer`)中的电子表格字节中提取数据。
    
    
    _从本地文件读取电子表格字节并提取数据_```js
    var workbook = XLSX.readFile(filename, opts);
    

    readFile 方法尝试读取指定路径的电子表格文件。 浏览器通常不允许以这种方式读取文件(这被视为安全风险),尝试以这种方式读取文件将抛出错误。

    第二个参数 opts 是可选的。"解析选项" 涵盖了支持的属性和行为。

    示例

    以下是一些常见场景(点击每个副标题查看代码):

    在 NodeJS 服务器中的本地文件(点击展开)

    readFile 在底层使用 fs.readFileSync:```js var XLSX = require("xlsx");

    var workbook = XLSX.readFile("test.xlsx");

    root@kitploit:~
    对于 Node ESM 环境,`readFile` 辅助函数未启用。相反,应使用 `fs.readFileSync` 将文件数据读取为 `Buffer`,以便与 `XLSX.read` 配合使用:```js
    import { readFileSync } from "fs";
    import { read } from "xlsx/xlsx.mjs";
    
    const buf = readFileSync("test.xlsx");
    /* buf is a Buffer */
    const workbook = read(buf);
    
    在 Deno 应用中加载本地文件(点击展开)

    readFile 在内部使用 Deno.readFileSync:

    root@kitploit:~
    // @deno-types="https://deno.land/x/sheetjs/types/index.d.ts"
    import * as XLSX from 'https://deno.land/x/sheetjs/xlsx.mjs'
    
    const workbook = XLSX.readFile("test.xlsx");
    ```
    读取文件的应用程序必须通过 `--allow-read` 标志调用。  
    [`deno` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/deno/) 包含更多示例。
    
    </details>
    
    <details>
      <summary><b>网页中用户提交的文件("拖放")</b>(点击展开)</summary>
    
    对于目标为 Chrome 76+ 的现代网站,建议使用 `File#arrayBuffer`:```js
    // XLSX is a global from the standalone script
    
    async function handleDropAsync(e) {
      e.stopPropagation(); e.preventDefault();
      const f = e.dataTransfer.files[0];
      /* f is a File */
      const data = await f.arrayBuffer();
      /* data is an ArrayBuffer */
      const workbook = XLSX.read(data);
    
      /* DO SOMETHING WITH workbook HERE */
    }
    drop_dom_element.addEventListener("drop", handleDropAsync, false);
    ```
    为获得最大兼容性,应使用 `FileReader` API:```js
    function handleDrop(e) {
      e.stopPropagation(); e.preventDefault();
      var f = e.dataTransfer.files[0];
      /* f is a File */
      var reader = new FileReader();
      reader.onload = function(e) {
        var data = e.target.result;
        /* reader.readAsArrayBuffer(file) -> data will be an ArrayBuffer */
        var workbook = XLSX.read(data);
    
        /* DO SOMETHING WITH workbook HERE */
      };
      reader.readAsArrayBuffer(f);
    }
    drop_dom_element.addEventListener("drop", handleDrop, false);
    ```
    <https://oss.sheetjs.com/sheetjs/> 演示了 FileReader 技术。
    
    </details>
    
    <details>
      <summary><b>用户提交的文件,使用 HTML INPUT 元素</b>(点击展开)</summary>
    
    从一个 `type="file"` 的 HTML INPUT 元素开始:```html
    <input type="file" id="input_dom_element">
    ```
    对于针对 Chrome 76+ 的现代网站,推荐使用 `Blob#arrayBuffer`:```js
    // XLSX is a global from the standalone script
    
    async function handleFileAsync(e) {
      const file = e.target.files[0];
      const data = await file.arrayBuffer();
      /* data is an ArrayBuffer */
      const workbook = XLSX.read(data);
    
      /* DO SOMETHING WITH workbook HERE */
    }
    input_dom_element.addEventListener("change", handleFileAsync, false);
    ```
    对于更广泛的浏览器支持(包括IE10+),推荐使用`FileReader`方法:```js
    function handleFile(e) {
      var file = e.target.files[0];
      var reader = new FileReader();
      reader.onload = function(e) {
        var data = e.target.result;
        /* reader.readAsArrayBuffer(file) -> data will be an ArrayBuffer */
        var workbook = XLSX.read(e.target.result);
    
        /* DO SOMETHING WITH workbook HERE */
      };
      reader.readAsArrayBuffer(file);
    }
    input_dom_element.addEventListener("change", handleFile, false);
    ```
    该 [`oldie` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/oldie/) 展示了一个兼容 IE 的回退方案。
    
    </details>
    
    <details>
      <summary><b>在网页浏览器中获取文件("Ajax")</b>(点击展开)</summary>
    
    对于针对 Chrome 42+ 的现代网站,推荐使用 `fetch`:```js
    // XLSX is a global from the standalone script
    
    (async() => {
      const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
      const data = await (await fetch(url)).arrayBuffer();
      /* data is an ArrayBuffer */
      const workbook = XLSX.read(data);
    
      /* DO SOMETHING WITH workbook HERE */
    })();
    ```
    为了获得更广泛的支持,推荐使用 `XMLHttpRequest` 方法:```js
    var url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
    
    /* set up async GET request */
    var req = new XMLHttpRequest();
    req.open("GET", url, true);
    req.responseType = "arraybuffer";
    
    req.onload = function(e) {
      var workbook = XLSX.read(req.response);
    
      /* DO SOMETHING WITH workbook HERE */
    };
    
    req.send();
    ```
    The [`xhr` demo](https://github.com/weareu/xlsx/blob/HEAD/demos/xhr/) 包含了更详细的讨论和更多示例。
    
    <http://oss.sheetjs.com/sheetjs/ajax.html> 展示了针对 IE6+ 的回退方法。
    
    </details>
    
    <details>
      <summary><b>Photoshop 或 InDesign 插件中的本地文件</b>(点击显示)</summary>
    
    `readFile` 封装了 Photoshop 及其他 ExtendScript 目标中的 `File` 逻辑。指定的路径应为绝对路径:```js
    #include "xlsx.extendscript.js"
    
    /* Read test.xlsx from the Documents folder */
    var workbook = XLSX.readFile(Folder.myDocuments + "/test.xlsx");
    ```
    [`extendscript` demo](https://github.com/weareu/xlsx/blob/HEAD/demos/extendscript/) 包含一个更复杂的示例。
    
    </details>
    
    <details>
      <summary><b>Electron 应用中的本地文件</b> (点击展开)</summary>
    
    `readFile` 可以在渲染进程中使用:```js
    /* From the renderer process */
    var XLSX = require("xlsx");
    
    var workbook = XLSX.readFile(path);
    ```
    Electron API 已随时间变化。[`electron` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/electron/) 展示了一个完整示例并详细说明了所需的版本特定设置。
    
    </details>
    
    <details>
      <summary><b>在 React Native 移动应用中的本地文件</b> (click to show)</summary>
    
    [`react` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/react) 包含一个示例 React Native 应用。
    
    由于 React Native 不提供从文件系统读取文件的方法,必须使用第三方库。已测试以下库:
    
    - [`react-native-file-access`](https://npm.im/react-native-file-access)
    
    `base64` 编码返回与 `base64` 类型兼容的字符串:```js
    import XLSX from "xlsx";
    import { FileSystem } from "react-native-file-access";
    
    const b64 = await FileSystem.readFile(path, "base64");
    /* b64 is a base64 string */
    const workbook = XLSX.read(b64, {type: "base64"});
    ```
    - [`react-native-fs`](https://npm.im/react-native-fs)
    
    `ascii`编码返回与`binary`类型兼容的二进制字符串:```js
    import XLSX from "xlsx";
    import { readFile } from "react-native-fs";
    
    const bstr = await readFile(path, "ascii");
    /* bstr is a binary string */
    const workbook = XLSX.read(bstr, {type: "binary"});
    ```
    </details>
    
    <details>
      <summary><b>NodeJS 服务器文件上传</b>(点击展开)</summary>
    
    `read` 可以接受一个 NodeJS 缓冲区。`readFile` 可以读取由 HTTP POST 请求体解析器(例如 [`formidable`](https://npm.im/formidable))生成的文件:```js
    const XLSX = require("xlsx");
    const http = require("http");
    const formidable = require("formidable");
    
    const server = http.createServer((req, res) => {
      const form = new formidable.IncomingForm();
      form.parse(req, (err, fields, files) => {
        /* grab the first file */
        const f = Object.entries(files)[0][1];
        const path = f.filepath;
        const workbook = XLSX.readFile(path);
    
        /* DO SOMETHING WITH workbook HERE */
      });
    }).listen(process.env.PORT || 7262);
    ```
    [`server` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/server) 包含更高级的示例。
    
    </details>
    
    <details>
      <summary><b>在 NodeJS 进程中下载文件</b>(点击显示)</summary>
    
    Node 17.5 和 18.0 原生支持 fetch:```js
    const XLSX = require("xlsx");
    
    const data = await (await fetch(url)).arrayBuffer();
    /* data is an ArrayBuffer */
    const workbook = XLSX.read(data);
    ```
    为获得更广泛的兼容性,建议使用第三方模块。
    
    [`request`](https://npm.im/request) 需要设置编码为 `null` 以生成 Buffers:```js
    var XLSX = require("xlsx");
    var request = require("request");
    
    request({url: url, encoding: null}, function(err, resp, body) {
      var workbook = XLSX.read(body);
    
      /* DO SOMETHING WITH workbook HERE */
    });
    ```
    [`axios`](https://npm.im/axios) 在浏览器和 NodeJS 中的工作方式相同:```js
    const XLSX = require("xlsx");
    const axios = require("axios");
    
    (async() => {
      const res = await axios.get(url, {responseType: "arraybuffer"});
      /* res.data is a Buffer */
      const workbook = XLSX.read(res.data);
    
      /* DO SOMETHING WITH workbook HERE */
    })();
    ```
    </details>
    
    <details>
      <summary><b>在 Electron 应用中下载文件</b>(点击显示)</summary>
    
    主进程中的 `net` 模块可以向外部资源发起 HTTP/HTTPS 请求。响应应使用 `Buffer.concat` 手动拼接:```js
    const XLSX = require("xlsx");
    const { net } = require("electron");
    
    const req = net.request(url);
    req.on("response", (res) => {
      const bufs = []; // this array will collect all of the buffers
      res.on("data", (chunk) => { bufs.push(chunk); });
      res.on("end", () => {
        const workbook = XLSX.read(Buffer.concat(bufs));
    
        /* DO SOMETHING WITH workbook HERE */
      });
    });
    req.end();
    ```
    </details>
    
    <details>
      <summary><b>NodeJS 中的可读流</b>(点击展开)</summary>
    
    在处理可读流时,最简单的方法是将流缓冲起来,然后在最后处理整个内容:```js
    var fs = require("fs");
    var XLSX = require("xlsx");
    
    function process_RS(stream, cb) {
      var buffers = [];
      stream.on("data", function(data) { buffers.push(data); });
      stream.on("end", function() {
        var buffer = Buffer.concat(buffers);
        var workbook = XLSX.read(buffer, {type:"buffer"});
    
        /* DO SOMETHING WITH workbook IN THE CALLBACK */
        cb(workbook);
      });
    }
    ```
    </details>
    
    <details>
      <summary><b>浏览器中的 ReadableStream</b>(点击展开)</summary>
    
    当处理 `ReadableStream` 时,最简单的方法是将流缓冲起来,然后一次性处理整个数据:```js
    // XLSX is a global from the standalone script
    
    async function process_RS(stream) {
      /* collect data */
      const buffers = [];
      const reader = stream.getReader();
      for(;;) {
        const res = await reader.read();
        if(res.value) buffers.push(res.value);
        if(res.done) break;
      }
    
      /* concat */
      const out = new Uint8Array(buffers.reduce((acc, v) => acc + v.length, 0));
    
      let off = 0;
      for(const u8 of arr) {
        out.set(u8, off);
        off += u8.length;
      }
    
      return out;
    }
    
    const data = await process_RS(stream);
    /* data is Uint8Array */
    const workbook = XLSX.read(data);
    ```
    </details>
    
    更多详细示例请参见[包含的演示](https://github.com/weareu/xlsx/blob/HEAD/demos/)
    
    ### 处理 JSON 和 JS 数据
    
    JSON 和 JS 数据通常表示单个工作表。本节将使用一些实用函数来生成工作簿。
    
    _创建一个新工作簿_```js
    var workbook = XLSX.utils.book_new();
    ```
    `book_new` 实用函数创建一个没有工作表的工作簿。
    
    电子表格软件通常至少需要一个工作表,并在用户界面中强制执行这一要求。此库在写入时强制执行该要求,如果向写入函数传递空工作簿,则会抛出错误。
    
    **API**
    
    _从 JS 值数组的数组创建工作表_```js
    var worksheet = XLSX.utils.aoa_to_sheet(aoa, opts);
    ```
    `aoa_to_sheet`实用函数按行优先顺序遍历一个“数组的数组”,生成一个工作表对象。以下代码片段生成一个工作表,其中单元格`A1`设置为字符串`A1`,单元格`B1`设置为`B1`,等等:```js
    var worksheet = XLSX.utils.aoa_to_sheet([
      ["A1", "B1", "C1"],
      ["A2", "B2", "C2"],
      ["A3", "B3", "C3"]
    ]);
    ```
    ["Array of Arrays Input"](#array-of-arrays-input) 更详细地描述了该函数和可选的 `opts` 参数。
    
    
    _从 JS 对象数组创建工作表_```js
    var worksheet = XLSX.utils.json_to_sheet(jsa, opts);
    ```
    `json_to_sheet` 工具函数按顺序遍历JS对象数组,生成一个工作表对象。默认情况下,它会生成一个标题行和数组中每个对象对应的一行。可选的 `opts` 参数包含控制列顺序和标题输出的设置。
    
    ["对象数组输入"](#array-of-arrays-input) 更详细地描述了函数和可选的 `opts` 参数。
    
    **示例**
    
    ["SheetJS 之道"](#the-zen-of-sheetjs) 包含一个详细示例“从JSON端点获取数据并生成工作簿”
    
    [`x-spreadsheet`](https://github.com/myliang/x-spreadsheet) 是一个交互式数据网格,用于在浏览器中预览和修改结构化数据。[`xspreadsheet` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/xspreadsheet) 包含一个示例脚本,其中包含 `xtos` 函数,用于将 x-spreadsheet 数据对象转换为工作簿。<https://oss.sheetjs.com/sheetjs/x-spreadsheet> 是一个在线演示。
    
    <details>
      <summary><b>数据库查询(SQL或NoSQL)的记录</b>(点击显示)</summary>
    
    [`database` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/database/) 包含使用数据库和查询结果的示例。
    
    </details>
    
    <details>
      <summary><b>使用TensorFlow.js进行数值计算</b>(点击显示)</summary>
    
    [`@tensorflow/tfjs`](https://github.com/weareu/xlsx/blob/HEAD/@tensorflow/tfjs) 和其他库期望数据以简单数组的形式提供,非常适用于每列是一个数据向量的工作表。这与大多数人使用电子表格的方式相反,通常每行是一个向量。
    
    从 `tfjs` 恢复数据时,返回的数据点存储在类型化数组中。可以通过循环构造一个数组的数组。`Array#unshift` 可以在转换前预置一个标题行:```js
    const XLSX = require("xlsx");
    const tf = require('@tensorflow/tfjs');
    
    /* suppose xs and ys are vectors (1D tensors) -> tfarr will be a typed array */
    const tfdata = tf.stack([xs, ys]).transpose();
    const shape = tfdata.shape;
    const tfarr = tfdata.dataSync();
    
    /* construct the array of arrays */
    const aoa = [];
    for(let j = 0; j < shape[0]; ++j) {
      aoa[j] = [];
      for(let i = 0; i < shape[1]; ++i) aoa[j][i] = tfarr[j * shape[1] + i];
    }
    /* add headers to the top */
    aoa.unshift(["x", "y"]);
    
    /* generate worksheet */
    const worksheet = XLSX.utils.aoa_to_sheet(aoa);
    ```
    The [`array` demo](https://github.com/weareu/xlsx/blob/HEAD/demos/array/) shows a complete example.
    
    </details>
    
    
    ### 处理HTML表格
    
    **API**
    
    _通过抓取页面中的HTML TABLE创建工作表_```js
    var worksheet = XLSX.utils.table_to_sheet(dom_element, opts);
    ```
    The `table_to_sheet` 实用函数接受一个 DOM TABLE 元素并遍历行以生成工作表。`opts` 参数是可选的。
    ["HTML Table 输入"](#html-table-input) 更详细地描述了该函数。
    
    
    
    _通过抓取页面中的 HTML TABLE 创建工作簿_```js
    var workbook = XLSX.utils.table_to_book(dom_element, opts);
    ```
    `table_to_book` 工具函数遵循与 `table_to_sheet` 相同的逻辑。生成工作表后,它会创建一个空白工作簿并附加该电子表格。
    
    `options` 参数支持与 `table_to_sheet` 相同的选项,并额外增加了一个 `sheet` 属性,用于控制工作表名称。如果缺少该属性或未指定任何选项,则默认使用名称 `Sheet1`。
    
    **示例**
    
    以下是一些常见场景(点击每个副标题查看代码):
    
    <details>
      <summary><b>网页中的 HTML TABLE 元素</b>(点击展开)</summary>```html
    <!-- include the standalone script and shim.  this uses the UNPKG CDN -->
    <script src="https://unpkg.com/xlsx/dist/shim.min.js"></script>
    <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
    
    <!-- example table with id attribute -->
    <table id="tableau">
      <tr><td>Sheet</td><td>JS</td></tr>
      <tr><td>12345</td><td>67</td></tr>
    </table>
    
    <!-- this block should appear after the table HTML and the standalone script -->
    <script type="text/javascript">
      var workbook = XLSX.utils.table_to_book(document.getElementById("tableau"));
    
      /* DO SOMETHING WITH workbook HERE */
    </script>
    ```
    网页上的多个表格可以转换为单独的工作表:```js
    /* create new workbook */
    var workbook = XLSX.utils.book_new();
    
    /* convert table "table1" to worksheet named "Sheet1" */
    var sheet1 = XLSX.utils.table_to_sheet(document.getElementById("table1"));
    XLSX.utils.book_append_sheet(workbook, sheet1, "Sheet1");
    
    /* convert table "table2" to worksheet named "Sheet2" */
    var sheet2 = XLSX.utils.table_to_sheet(document.getElementById("table2"));
    XLSX.utils.book_append_sheet(workbook, sheet2, "Sheet2");
    
    /* workbook now has 2 worksheets */
    ```
    或者,可以提取并解析HTML代码:```js
    var htmlstr = document.getElementById("tableau").outerHTML;
    var workbook = XLSX.read(htmlstr, {type:"string"});
    ```
    </details>
    
    <details>
      <summary><b>Chrome/Chromium 扩展程序</b>(点击展开)</summary>
    
    演示 [`chrome` demo](https://github.com/weareu/xlsx/blob/HEAD/demos/chrome/) 展示了一个完整示例,并详细说明了所需的权限和其他设置。
    
    在扩展程序中,建议在内容脚本中生成工作簿,然后将对象传回扩展程序:```js
    /* in the worker script */
    chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
      /* pass a message like { sheetjs: true } from the extension to scrape */
      if(!msg || !msg.sheetjs) return;
      /* create a new workbook */
      var workbook = XLSX.utils.book_new();
      /* loop through each table element */
      var tables = document.getElementsByTagName("table")
      for(var i = 0; i < tables.length; ++i) {
        var worksheet = XLSX.utils.table_to_sheet(tables[i]);
        XLSX.utils.book_append_sheet(workbook, worksheet, "Table" + i);
      }
      /* pass back to the extension */
      return cb(workbook);
    });
    ```
    </details>
    
    <details>
      <summary><b>使用无头Chrome的服务端HTML表格</b> (点击展开)</summary>
    
    该[`headless` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/headless/)包含一个完整的演示,将HTML文件转换为XLSB工作簿。核心思想是将脚本添加到页面,在页面上下文中解析表格,生成一个`base64`工作簿并将其发送回以供进一步处理:```js
    const XLSX = require("xlsx");
    const { readFileSync } = require("fs"), puppeteer = require("puppeteer");
    
    const url = `https://sheetjs.com/demos/table`;
    
    /* get the standalone build source (node_modules/xlsx/dist/xlsx.full.min.js) */
    const lib = readFileSync(require.resolve("xlsx/dist/xlsx.full.min.js"), "utf8");
    
    (async() => {
      /* start browser and go to web page */
      const browser = await puppeteer.launch();
      const page = await browser.newPage();
      await page.goto(url, {waitUntil: "networkidle2"});
    
      /* inject library */
      await page.addScriptTag({content: lib});
    
      /* this function `s5s` will be called by the script below, receiving the Base64-encoded file */
      await page.exposeFunction("s5s", async(b64) => {
        const workbook = XLSX.read(b64, {type: "base64" });
    
        /* DO SOMETHING WITH workbook HERE */
      });
    
      /* generate XLSB file in webpage context and send back result */
      await page.addScriptTag({content: `
        /* call table_to_book on first table */
        var workbook = XLSX.utils.table_to_book(document.querySelector("TABLE"));
    
        /* generate XLSX file */
        var b64 = XLSX.write(workbook, {type: "base64", bookType: "xlsb"});
    
        /* call "s5s" hook exposed from the node process */
        window.s5s(b64);
      `});
    
      /* cleanup */
      await browser.close();
    })();
    ```
    <details>
      <summary><b>使用无头WebKit处理服务端HTML表格</b>(点击展开)</summary>
    
    [`headless` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/headless/) 提供了一个完整的演示,展示如何使用 [PhantomJS](https://phantomjs.org/) 将 HTML 文件转换为 XLSB 工作簿。核心思路是:将脚本添加到页面,在页面上下文中解析表格,生成一个 `binary` 工作簿,然后将其发送回以进行进一步处理:
    </details>```js
    var XLSX = require('xlsx');
    var page = require('webpage').create();
    
    /* this code will be run in the page */
    var code = [ "function(){",
      /* call table_to_book on first table */
      "var wb = XLSX.utils.table_to_book(document.body.getElementsByTagName('table')[0]);",
    
      /* generate XLSB file and return binary string */
      "return XLSX.write(wb, {type: 'binary', bookType: 'xlsb'});",
    "}" ].join("");
    
    page.open('https://sheetjs.com/demos/table', function() {
      /* Load the browser script from the UNPKG CDN */
      page.includeJs("https://unpkg.com/xlsx/dist/xlsx.full.min.js", function() {
        /* The code will return an XLSB file encoded as binary string */
        var bin = page.evaluateJavaScript(code);
    
        var workbook = XLSX.read(bin, {type: "binary"});
        /* DO SOMETHING WITH workbook HERE */
    
        phantom.exit();
      });
    });
    ```
    </details>
    
    <details>
      <summary><b>无浏览器的NodeJS HTML表格</b>(点击展开)</summary>
    
    NodeJS不包含DOM实现,而Puppeteer需要一个庞大的Chromium构建。  [`jsdom`](https://npm.im/jsdom)是一个轻量级的替代方案:```js
    const XLSX = require("xlsx");
    const { readFileSync } = require("fs");
    const { JSDOM } = require("jsdom");
    
    /* obtain HTML string.  This example reads from test.html */
    const html_str = fs.readFileSync("test.html", "utf8");
    /* get first TABLE element */
    const doc = new JSDOM(html_str).window.document.querySelector("table");
    /* generate workbook */
    const workbook = XLSX.utils.table_to_book(doc);
    ```
    </details>
    
    ## 处理数据
    
    ["Common Spreadsheet Format"](#common-spreadsheet-format) 是对工作簿核心概念的简单对象表示。这些实用函数处理对象表示,旨在处理常见用例。
    
    ### 修改工作簿结构
    
    **API**
    
    _向工作簿追加工作表_```js
    XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
    ```
    `book_append_sheet` 工具函数用于向工作簿追加一个工作表。
    第三个参数指定所需的工作表名称。可通过多次调用该函数向工作簿添加多个工作表。
    如果指定的工作表名称已存在于工作簿中,则会抛出错误。
    
    _向工作簿追加工作表并查找唯一名称_```js
    var new_name = XLSX.utils.book_append_sheet(workbook, worksheet, name, true);
    ```
    如果第四个参数为 `true`,函数将从指定的工作表名称开始。如果工作簿中已存在该工作表名称,将通过查找名称词干并递增计数器来选择新的工作表名称:```js
    XLSX.utils.book_append_sheet(workbook, sheetA, "Sheet2", true); // Sheet2
    XLSX.utils.book_append_sheet(workbook, sheetB, "Sheet2", true); // Sheet3
    XLSX.utils.book_append_sheet(workbook, sheetC, "Sheet2", true); // Sheet4
    XLSX.utils.book_append_sheet(workbook, sheetD, "Sheet2", true); // Sheet5
    ```
    _按标签顺序列出工作表名称_```js
    var wsnames = workbook.SheetNames;
    ```
    工作簿对象的 `SheetNames` 属性是一个按"标签顺序"排列的工作表名称列表。API 函数将查看此数组。
    
    _就地替换工作表_```js
    workbook.Sheets[sheet_name] = new_worksheet;
    ```
    `Sheets` 属性是 workbook 对象的一个属性,它是一个对象,其键名是工作表名称,键值是工作表对象。通过重新赋值给 `Sheets` 对象的某个属性,可以更改工作表对象,而不会破坏工作表的其余结构。
    
    **示例**
    
    <details>
      <summary><b>向工作簿添加新工作表</b>(点击展开)</summary>
    
    本示例使用了 [`XLSX.utils.aoa_to_sheet`](#array-of-arrays-input)。```js
    var ws_name = "SheetJS";
    
    /* Create worksheet */
    var ws_data = [
      [ "S", "h", "e", "e", "t", "J", "S" ],
      [  1 ,  2 ,  3 ,  4 ,  5 ]
    ];
    var ws = XLSX.utils.aoa_to_sheet(ws_data);
    
    /* Add the worksheet to the workbook */
    XLSX.utils.book_append_sheet(wb, ws, ws_name);
    ```
    </details>
    
    ### 修改单元格值
    
    **API**
    
    _修改工作表中的单个单元格值_```js
    XLSX.utils.sheet_add_aoa(worksheet, [[new_value]], { origin: address });
    ```
    _修改工作表中的多个单元格值_```js
    XLSX.utils.sheet_add_aoa(worksheet, aoa, opts);
    ```
    `sheet_add_aoa` 实用函数用于修改工作表中的单元格值。  第一个参数是工作表对象。  第二个参数是一个值的数组的数组。  第三个参数的 `origin` 键控制单元格写入的位置。  以下代码片段设置了 `B3=1` 和 `E5="abc"`:```js
    XLSX.utils.sheet_add_aoa(worksheet, [
      [1],                             // <-- Write 1 to cell B3
      ,                                // <-- Do nothing in row 4
      [/*B5*/, /*C5*/, /*D5*/, "abc"]  // <-- Write "abc" to cell E5
    ], { origin: "B3" });
    ```
    ["数组输入"](#array-of-arrays-input) 更详细地描述了该函数及其可选的 `opts` 参数。
    
    **示例**
    
    <details>
      <summary><b>向工作表追加行</b>(点击展开)</summary>
    
    特殊起始值 `-1` 指示 `sheet_add_aoa` 从区域最后一行的下一行 A 列开始,追加数据:```js
    XLSX.utils.sheet_add_aoa(worksheet, [
      ["first row after data", 1],
      ["second row after data", 2]
    ], { origin: -1 });
    ```
    </details>
    
    
    ### 修改其他工作表/工作簿/单元格属性
    
    ["Common Spreadsheet Format"](#common-spreadsheet-format) 部分更详细地描述了对象结构。
    
    ## 打包和发布数据
    
    ### 写入工作簿
    
    **API**
    
    _根据数据生成电子表格字节(文件)_```js
    var data = XLSX.write(workbook, opts);
    ```
    `write` 方法尝试将工作簿中的数据打包到内存中的一个文件中。默认情况下,生成 XLSX 文件,但可以通过 `opts` 参数的 `bookType` 属性来控制。根据 `type` 选项,数据可以存储为“二进制字符串”、JS 字符串、`Uint8Array` 或 Buffer。
    
    第二个 `opts` 参数是必需的。["写入选项"](#writing-options)涵盖了支持的属性和行为。
    
    _生成并尝试保存文件_```js
    XLSX.writeFile(workbook, filename, opts);
    ```
    `writeFile` 方法打包数据并尝试保存新文件。导出文件格式由 `filename` 的扩展名决定(`SheetJS.xlsx` 表示 XLSX 导出,`SheetJS.xlsb` 表示 XLSB 导出等)。
    
    `writeFile` 方法使用平台特定的 API 来启动文件保存。在 NodeJS 中,`fs.readFileSync` 可以创建文件。在 Web 浏览器中,尝试使用 HTML5 `download` 属性进行下载,对于 IE 有回退方案。
    
    _生成并尝试保存一个 XLSX 文件_```js
    XLSX.writeFileXLSX(workbook, filename, opts);
    ```
    `writeFile` 方法内嵌了许多不同的导出函数。这对开发者体验很好,但不适合使用当前开发者工具进行摇树优化(tree shaking)。当仅需要导出 XLSX 时,此方法避免引用其他导出函数。
    
    第二个参数 `opts` 是可选的。["Writing Options"](#writing-options) 涵盖了支持的属性和行为。
    
    **示例**
    
    <details>
      <summary><b>NodeJS 服务器中的本地文件</b>(点击展开)</summary>
    
    在服务器环境中,`writeFile` 使用 `fs.writeFileSync`:```js
    var XLSX = require("xlsx");
    
    /* output format determined by filename */
    XLSX.writeFile(workbook, "out.xlsb");
    ```
    对于 Node ESM,`writeFile` 辅助函数未启用。相反,应使用 `fs.writeFileSync` 将文件数据写入 `Buffer`,以便与 `XLSX.write` 一起使用:```js
    import { writeFileSync } from "fs";
    import { write } from "xlsx/xlsx.mjs";
    
    const buf = write(workbook, {type: "buffer", bookType: "xlsb"});
    /* buf is a Buffer */
    const workbook = writeFileSync("out.xlsb", buf);
    ```
    </details>
    
    <details>
      <summary><b>Deno 应用程序中的本地文件</b> (点击展开)</summary>
    
    `writeFile` 使用 `Deno.writeFileSync` 内部实现:```js
    // @deno-types="https://deno.land/x/sheetjs/types/index.d.ts"
    import * as XLSX from 'https://deno.land/x/sheetjs/xlsx.mjs'
    
    XLSX.writeFile(workbook, "test.xlsx");
    ```
    写入文件的应用程序必须使用 `--allow-write` 标志来调用。更多示例请参见 [`deno` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/deno/)。
    
    </details>
    
    <details>
      <summary><b>在 PhotoShop 或 InDesign 插件中的本地文件</b>(点击展开)</summary>
    
    `writeFile` 封装了 Photoshop 和其他 ExtendScript 目标中的 `File` 逻辑。指定的路径应为绝对路径:```js
    #include "xlsx.extendscript.js"
    
    /* output format determined by filename */
    XLSX.writeFile(workbook, "out.xlsx");
    /* at this point, out.xlsx is a file that you can distribute */
    ```
    [`extendscript` 示例](https://github.com/weareu/xlsx/blob/HEAD/demos/extendscript/) 包含一个更复杂的例子。
    
    </details>
    
    <details>
      <summary><b>在浏览器中下载文件到用户机器</b>(点击展开)</summary>
    
    `XLSX.writeFile` 封装了几种触发文件保存的技术:
    
    - `URL` 浏览器 API 为文件创建一个对象 URL,库通过创建链接并强制点击来使用它。它在现代浏览器中得到支持。
    - `msSaveBlob` 是用于触发文件保存的 IE10+ API。
    - `IE_FileSave` 使用 VBScript 和 ActiveX 在 Windows XP 和 Windows 7 上的 IE6+ 中写入文件。shim 必须包含在包含的 HTML 页面中。
    
    没有标准的方法来确定实际文件是否已下载。```js
    /* output format determined by filename */
    XLSX.writeFile(workbook, "out.xlsb");
    /* at this point, out.xlsb will have been downloaded */
    ```
    </details>
    
    <details>
      <summary><b>在旧版浏览器中下载文件</b>(点击展开)</summary>
    
    `XLSX.writeFile` 技术适用于大多数现代浏览器以及旧版 IE。
    对于更早的浏览器,可以通过包装库实现相应的解决方案。
    
    [`FileSaver.js`](https://github.com/eligrey/FileSaver.js/) 实现了 `saveAs` 功能。
    注意:如果可用,`XLSX.writeFile` 会自动调用 `saveAs`。```js
    /* bookType can be any supported output type */
    var wopts = { bookType:"xlsx", bookSST:false, type:"array" };
    
    var wbout = XLSX.write(workbook,wopts);
    
    /* the saveAs call downloads a file on the local machine */
    saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
    ```
    [`Downloadify`](https://github.com/dcneiner/downloadify) 使用 Flash SWF 按钮生成本地文件,适用于无法使用 ActiveX 的环境:```js
    Downloadify.create(id,{
      /* other options are required! read the downloadify docs for more info */
      filename: "test.xlsx",
      data: function() { return XLSX.write(wb, {bookType:"xlsx", type:"base64"}); },
      append: false,
      dataType: "base64"
    });
    ```
    The [`oldie` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/oldie/) 展示了一个兼容 IE 的回退场景。
    
    </details>
    
    <details>
      <summary><b>浏览器上传文件 (ajax)</b> (点击展开)</summary>
    
    一个使用 XHR 的完整示例 [包含在 XHR 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/xhr/) 中,同时还有使用 fetch 和包装库的示例。此示例假设服务器能够处理 Base64 编码的文件(请参阅演示中的基本 nodejs 服务器):```js
    /* in this example, send a base64 string to the server */
    var wopts = { bookType:"xlsx", bookSST:false, type:"base64" };
    
    var wbout = XLSX.write(workbook,wopts);
    
    var req = new XMLHttpRequest();
    req.open("POST", "/upload", true);
    var formdata = new FormData();
    formdata.append("file", "test.xlsx"); // <-- server expects `file` to hold name
    formdata.append("data", wbout); // <-- `data` holds the base64-encoded data
    req.send(formdata);
    ```
    </details>
    
    <details>
      <summary><b>PhantomJS (Headless Webkit) 文件生成</b> (点击展开)</summary>
    
    [`headless` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/headless/) 包含一个完整的演示,用于使用 [PhantomJS](https://phantomjs.org/) 将 HTML 文件转换为 XLSB 工作簿。PhantomJS 的 `fs.write` 支持从主进程写入文件,但其接口与 NodeJS 的 `fs` 模块不同:```js
    var XLSX = require('xlsx');
    var fs = require('fs');
    
    /* generate a binary string */
    var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsx" });
    /* write to file */
    fs.write("test.xlsx", bin, "wb");
    ```
    注意:["处理 HTML 表格"](#processing-html-tables) 部分展示了如何在"Headless WebKit"中从页面上的 HTML 表格生成工作簿。
    
    </details>
    
    附带的[示例](https://github.com/weareu/xlsx/blob/HEAD/demos/)涵盖了移动应用和其他特殊部署。
    
    ### 写入示例
    
    - <http://sheetjs.com/demos/table.html> 导出一个 HTML 表格
    - <http://sheetjs.com/demos/writexlsx.html> 生成一个简单的文件
    
    ### 流式写入
    
    流式写入函数可在 `XLSX.stream` 对象中使用。它们接受与普通写入函数相同的参数,但返回一个 NodeJS 可读流。
    
    - `XLSX.stream.to_csv` 是 `XLSX.utils.sheet_to_csv` 的流式版本。
    - `XLSX.stream.to_html` 是 `XLSX.utils.sheet_to_html` 的流式版本。
    - `XLSX.stream.to_json` 是 `XLSX.utils.sheet_to_json` 的流式版本。
    
    <details>
      <summary><b>nodejs 转换为 CSV 并写入文件</b> (点击展开)</summary>```js
    var output_file_name = "out.csv";
    var stream = XLSX.stream.to_csv(worksheet);
    stream.pipe(fs.createWriteStream(output_file_name));
    ```
    </details>
    
    <details>
      <summary><b>nodejs 将 JSON 流写入屏幕</b> (点击显示)</summary>```js
    /* to_json returns an object-mode stream */
    var stream = XLSX.stream.to_json(worksheet, {raw:true});
    
    /* the following stream converts JS objects to text via JSON.stringify */
    var conv = new Transform({writableObjectMode:true});
    conv._transform = function(obj, e, cb){ cb(null, JSON.stringify(obj) + "\n"); };
    
    stream.pipe(conv); conv.pipe(process.stdout);
    ```
    </details>
    
    <details>
      <summary><b>导出 NUMBERS 文件</b> (点击显示)</summary>
    
    NUMBERS 写入器需要一个相当大的基础。补充的 `xlsx.zahl` 脚本提供支持。`xlsx.zahl.js` 专为独立使用和 NodeJS 使用设计,而 `xlsx.zahl.mjs` 适用于 ESM。
    
    _浏览器_```html
    <meta charset="utf8">
    <script src="xlsx.full.min.js"></script>
    <script src="xlsx.zahl.js"></script>
    <script>
    var wb = XLSX.utils.book_new(); var ws = XLSX.utils.aoa_to_sheet([
      ["SheetJS", "<3","விரிதாள்"],
      [72,,"Arbeitsblätter"],
      [,62,"数据"],
      [true,false,],
    ]); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
    XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL, compression: true});
    </script>
    ```
    _Node_```js
    var XLSX = require("./xlsx.flow");
    var XLSX_ZAHL = require("./dist/xlsx.zahl");
    var wb = XLSX.utils.book_new(); var ws = XLSX.utils.aoa_to_sheet([
      ["SheetJS", "<3","விரிதாள்"],
      [72,,"Arbeitsblätter"],
      [,62,"数据"],
      [true,false,],
    ]); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
    XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL, compression: true});
    ```
    _Deno_```ts
    import * as XLSX from './xlsx.mjs';
    import XLSX_ZAHL from './dist/xlsx.zahl.mjs';
    
    var wb = XLSX.utils.book_new(); var ws = XLSX.utils.aoa_to_sheet([
      ["SheetJS", "<3","விரிதாள்"],
      [72,,"Arbeitsblätter"],
      [,62,"数据"],
      [true,false,],
    ]); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
    XLSX.writeFile(wb, "textports.numbers", {numbers: XLSX_ZAHL, compression: true});
    ```
    </details>
    
    <https://github.com/sheetjs/sheetaki> 将写入流管道传输到 Node.js 响应。
    
    ### 生成 JSON 和 JS 数据
    
    JSON 和 JS 数据通常表示单个工作表。本节中的实用函数适用于单个工作表。
    
    ["Common Spreadsheet Format"](#common-spreadsheet-format) 部分更详细地描述了对象结构。`workbook.SheetNames` 是工作表名称的有序列表。`workbook.Sheets` 是一个对象,其键是工作表名称,其值是工作表对象。
    
    "第一个工作表" 存储在 `workbook.Sheets[workbook.SheetNames[0]]`。
    
    **API**
    
    _从工作表创建一个 JS 对象数组_```js
    var jsa = XLSX.utils.sheet_to_json(worksheet, opts);
    ```
    _从工作表中创建一个由JS值组成的数组的数组_```js
    var aoa = XLSX.utils.sheet_to_json(worksheet, {...opts, header: 1});
    ```
    The `sheet_to_json` 实用函数按行优先顺序遍历工作簿,生成一个对象数组。第二个参数 `opts` 控制多个导出决策,包括值的类型(JS 值或格式化文本)。["JSON"](#json) 章节更详细地描述了该参数。
    
    默认情况下,`sheet_to_json` 扫描第一行并将这些值用作标题。使用 `header: 1` 选项,该函数会导出一个值数组的数组。
    
    **示例**
    
    [`x-spreadsheet`](https://github.com/myliang/x-spreadsheet) 是一个交互式数据网格,用于在 Web 浏览器中预览和修改结构化数据。[`xspreadsheet` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/xspreadsheet) 包含一个示例脚本,其中包含用于将工作簿转换为 x-spreadsheet 数据对象的 `stox` 函数。<https://oss.sheetjs.com/sheetjs/x-spreadsheet> 是一个在线演示。
    
    <details>
      <summary><b>在 React 数据网格中预览数据</b>(点击展开)</summary>
    
    [`react-data-grid`](https://npm.im/react-data-grid) 是一个专为 React 定制的数据网格。它期望两个属性:`rows`(数据对象数组)和 `columns`(描述列的数组)。为了调整数据以适应 React 数据网格 API,从数组的数组开始是最简单的。
    
    此演示通过获取远程文件并使用 `XLSX.read` 提取开始:```js
    import { useEffect, useState } from "react";
    import DataGrid from "react-data-grid";
    import { read, utils } from "xlsx";
    
    const url = "https://oss.sheetjs.com/test_files/RkNumber.xls";
    
    export default function App() {
      const [columns, setColumns] = useState([]);
      const [rows, setRows] = useState([]);
      useEffect(() => {(async () => {
        const wb = read(await (await fetch(url)).arrayBuffer(), { WTF: 1 });
    
        /* use sheet_to_json with header: 1 to generate an array of arrays */
        const data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1 });
    
        /* see react-data-grid docs to understand the shape of the expected data */
        setColumns(data[0].map((r) => ({ key: r, name: r })));
        setRows(data.slice(1).map((r) => r.reduce((acc, x, i) => {
          acc[data[0][i]] = x;
          return acc;
        }, {})));
      })(); });
    
      return <DataGrid columns={columns} rows={rows} />;
    }
    ```
    </details>
    
    <details>
      <summary><b>在VueJS数据网格中预览数据</b>(点击展开)</summary>
    
    [`vue3-table-lite`](https://github.com/linmasahiro/vue3-table-lite) 是一个简单的VueJS 3数据表格。它在[VueJS演示](https://github.com/weareu/xlsx/blob/HEAD/demos/vue/modify/)中展示。
    
    </details>
    
    <details>
      <summary><b>填充数据库(SQL或NoSQL)</b>(点击展开)</summary>
    
    [`database`演示](https://github.com/weareu/xlsx/blob/HEAD/demos/database/)包含使用数据库和查询结果的示例。
    
    </details>
    
    <details>
      <summary><b>使用TensorFlow.js进行数值计算</b>(点击展开)</summary>
    
    [`@tensorflow/tfjs`](https://github.com/weareu/xlsx/blob/HEAD/@tensorflow/tfjs)和其他库期望数据以简单数组形式存在,非常适合每列是一个数据向量的工作表。这与大多数人对电子表格的使用方式相反,后者通常每行是一个向量。单个`Array#map`可以从`sheet_to_json`导出中提取命名的行:
    
    </details>```js
    const XLSX = require("xlsx");
    const tf = require('@tensorflow/tfjs');
    
    const key = "age"; // this is the field we want to pull
    const ages = XLSX.utils.sheet_to_json(worksheet).map(r => r[key]);
    const tf_data = tf.tensor1d(ages);
    ```
    所有字段可以一次性处理,使用由 `sheet_to_json` 导出(带 `header: 1`)生成的 2D 张量的转置。第一行如果包含标题标签,应通过切片移除:```js
    const XLSX = require("xlsx");
    const tf = require('@tensorflow/tfjs');
    
    /* array of arrays of the data starting on the second row */
    const aoa = XLSX.utils.sheet_to_json(worksheet, {header: 1}).slice(1);
    /* dataset in the "correct orientation" */
    const tf_dataset = tf.tensor2d(aoa).transpose();
    /* pull out each dataset with a slice */
    const tf_field0 = tf_dataset.slice([0,0], [1,tensor.shape[1]]).flatten();
    const tf_field1 = tf_dataset.slice([1,0], [1,tensor.shape[1]]).flatten();
    ```
    [`array` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/array/)展示了一个完整示例。
    
    </details>
    
    
    ### 生成HTML表格
    
    **API**
    
    _从工作表生成HTML表格_```js
    var html = XLSX.utils.sheet_to_html(worksheet);
    ```
    The `sheet_to_html` utility function generates HTML code based on the worksheet
    data.  Each cell in the worksheet is mapped to a `<TD>` element.  Merged cells
    in the worksheet are serialized by setting `colspan` and `rowspan` attributes.
    
    **Examples**
    
    The `sheet_to_html` utility function generates HTML code that can be added to
    any DOM element by setting the `innerHTML`:```js
    var container = document.getElementById("tavolo");
    container.innerHTML = XLSX.utils.sheet_to_html(worksheet);
    ```
    结合 `fetch`,从一个工作簿构建站点是简单直接的:
    
    <details>
      <summary><b>Vanilla JS + HTML 获取工作簿并生成表格预览</b>(点击显示)</summary>```html
    <body>
      <style>TABLE { border-collapse: collapse; } TD { border: 1px solid; }</style>
      <div id="tavolo"></div>
      <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
      <script type="text/javascript">
    (async() => {
      /* fetch and parse workbook -- see the fetch example for details */
      const workbook = XLSX.read(await (await fetch("sheetjs.xlsx")).arrayBuffer());
    
      let output = [];
      /* loop through the worksheet names in order */
      workbook.SheetNames.forEach(name => {
    
        /* generate HTML from the corresponding worksheets */
        const worksheet = workbook.Sheets[name];
        const html = XLSX.utils.sheet_to_html(worksheet);
    
        /* add a header with the title name followed by the table */
        output.push(`<H3>${name}</H3>${html}`);
      });
      /* write to the DOM at the end */
      tavolo.innerHTML = output.join("\n");
    })();
      </script>
    </body>
    ```
    </details>
    
    <details>
      <summary><b>React 获取工作簿并生成 HTML 表格预览</b> (点击展开)</summary>
    
    一般建议使用 React 友好的工作流,但也可以生成 HTML 并通过 `dangerouslySetInnerHTML` 在 React 中使用:```jsx
    function Tabeller(props) {
      /* the workbook object is the state */
      const [workbook, setWorkbook] = React.useState(XLSX.utils.book_new());
    
      /* fetch and update the workbook with an effect */
      React.useEffect(() => { (async() => {
        /* fetch and parse workbook -- see the fetch example for details */
        const wb = XLSX.read(await (await fetch("sheetjs.xlsx")).arrayBuffer());
        setWorkbook(wb);
      })(); });
    
      return workbook.SheetNames.map(name => (<>
        <h3>name</h3>
        <div dangerouslySetInnerHTML={{
          /* this __html mantra is needed to set the inner HTML */
          __html: XLSX.utils.sheet_to_html(workbook.Sheets[name])
        }} />
      </>));
    }
    ```
    [`react` 演示](https://github.com/weareu/xlsx/blob/HEAD/demos/react) 包含更多 React 示例。
    
    </details>
    
    <details>
      <summary><b>VueJS 获取工作簿并生成 HTML 表格预览</b>(点击展开)</summary>
    
    通常建议使用 VueJS 友好的工作流,但也可以生成 HTML 并在 VueJS 中使用 `v-html` 指令:```jsx
    import { read, utils } from 'xlsx';
    import { reactive } from 'vue';
    
    const S5SComponent = {
      mounted() { (async() => {
        /* fetch and parse workbook -- see the fetch example for details */
        const workbook = read(await (await fetch("sheetjs.xlsx")).arrayBuffer());
        /* loop through the worksheet names in order */
        workbook.SheetNames.forEach(name => {
          /* generate HTML from the corresponding worksheets */
          const html = utils.sheet_to_html(workbook.Sheets[name]);
          /* add to state */
          this.wb.wb.push({ name, html });
        });
      })(); },
      /* this state mantra is required for array updates to work */
      setup() { return { wb: reactive({ wb: [] }) }; },
      template: `
      <div v-for="ws in wb.wb" :key="ws.name">
        <h3>{{ ws.name }}</h3>
        <div v-html="ws.html"></div>
      </div>`
    };
    ```
    该 [`vuejs` 示例](https://github.com/weareu/xlsx/blob/HEAD/demos/vue) 包含了更多 React 示例。
    
    </details>
    
    ### 生成单工作表快照
    
    `sheet_to_*` 函数接受一个工作表对象。
    
    **API**
    
    _从单个工作表生成 CSV_```js
    var csv = XLSX.utils.sheet_to_csv(worksheet, opts);
    ```
    这个快照旨在复制“CSV UTF8 (`.csv`)”输出类型。
    ["分隔符分隔输出"](#delimiter-separated-output) 详细描述了该功能和可选的 `opts` 参数。
    
    _从单个工作表生成“Text”_```js
    var txt = XLSX.utils.sheet_to_txt(worksheet, opts);
    ```
    此快照旨在复制“UTF16文本(`.txt`)”输出类型。
    ["分隔符分隔的输出"](#delimiter-separated-output) 更详细地描述了该功能及可选的 `opts` 参数。
    
    _从单个工作表生成公式列表_```js
    var fmla = XLSX.utils.sheet_to_formulae(worksheet);
    ```
    此快照生成一个数组,每个条目代表嵌入的公式。数组公式以 `range=formula` 形式呈现,普通单元格以 `cell=formula or value` 形式呈现。字符串字面量以撇号 `'` 开头,与Excel的公式栏显示一致。
    
    ["公式输出"](#formulae-output) 详细描述了该函数。
    
    ## 接口
    
    `XLSX` 是在浏览器中暴露的变量,也是导出的 Node 变量。
    
    `XLSX.version` 是库的版本号(由构建脚本添加)。
    
    `XLSX.SSF` 是嵌入的[格式库](https://git.io/ssf)版本。
    
    ### 解析函数
    
    `XLSX.read(data, read_opts)` 尝试解析 `data`。
    
    `XLSX.readFile(filename, read_opts)` 尝试读取 `filename` 并解析。
    
    解析选项在[解析选项](#parsing-options)部分描述。
    
    ### 写入函数
    
    `XLSX.write(wb, write_opts)` 尝试写入工作簿 `wb`
    
    `XLSX.writeFile(wb, filename, write_opts)` 尝试将 `wb` 写入 `filename`。
    在基于浏览器的环境中,它将尝试强制客户端下载。
    
    `XLSX.writeFileAsync(wb, filename, o, cb)` 尝试将 `wb` 写入 `filename`。
    如果省略 `o`,写入器将使用第三个参数作为回调。
    
    `XLSX.stream` 包含一组流式写入函数。
    
    写入选项在[写入选项](#writing-options)部分描述。
    
    ### 工具函数
    
    工具函数位于 `XLSX.utils` 对象中,并在[工具函数](#utility-functions)部分描述:
    
    **构建:**
    
    - `book_new` 创建一个空工作簿
    - `book_append_sheet` 向工作簿添加工作表
    
    **导入:**
    
    - `aoa_to_sheet` 将JS数据的二维数组转换为工作表。
    - `json_to_sheet` 将JS对象数组转换为工作表。
    - `table_to_sheet` 将DOM TABLE元素转换为工作表。
    - `sheet_add_aoa` 向现有工作表添加JS数据的二维数组。
    - `sheet_add_json` 向现有工作表添加JS对象数组。
    
    **导出:**
    
    - `sheet_to_json` 将工作表对象转换为JSON对象数组。
    - `sheet_to_csv` 生成分隔符分隔值输出。
    - `sheet_to_txt` 生成UTF16格式文本。
    - `sheet_to_html` 生成HTML输出。
    - `sheet_to_formulae` 生成公式列表(包含值回退)。
    
    **单元格和单元格地址操作:**
    
    - `format_cell` 生成单元格的文本值(使用数字格式)。
    - `encode_row / decode_row` 在0索引行和1索引行之间转换。
    - `encode_col / decode_col` 在0索引列和列名之间转换。
    - `encode_cell / decode_cell` 转换单元格地址。
    - `encode_range / decode_range` 转换单元格范围。
    
    ## 通用电子表格格式
    
    SheetJS 符合通用电子表格格式 (CSF):
    
    ### 通用结构
    
    单元格地址对象存储为 `{c:C, r:R}`,其中 `C` 和 `R` 分别是0索引的列号和行号。例如,单元格地址 `B5` 由对象 `{c:1, r:4}` 表示。
    
    单元格范围对象存储为 `{s:S, e:E}`,其中 `S` 是范围的第一个单元格,`E` 是范围的最后一个单元格。范围是包含的。例如,范围 `A3:B7` 由对象 `{s:{c:0, r:2}, e:{c:1, r:6}}` 表示。工具函数按行优先顺序遍历工作表范围:```js
    for(var R = range.s.r; R <= range.e.r; ++R) {
      for(var C = range.s.c; C <= range.e.c; ++C) {
        var cell_address = {c:C, r:R};
        /* if an A1-style address is needed, encode the address */
        var cell_ref = XLSX.utils.encode_cell(cell_address);
      }
    }
    ```
    ### 单元格对象
    
    单元格对象是普通的 JS 对象,其键和值遵循以下约定:
    
    | Key | 描述                                                            |
    | --- | -------------------------------------------------------------- |
    | `v` | 原始值(有关更多信息,请参阅数据类型部分)                        |
    | `w` | 格式化文本(如果适用)                                           |
    | `t` | 类型:`b` 布尔值,`e` 错误,`n` 数字,`d` 日期,`s` 文本,`z` 存根 |
    | `f` | 单元格公式,编码为 A1 风格字符串(如果适用)                       |
    | `F` | 如果公式是数组公式,则包含数组范围(如果适用)                     |
    | `D` | 如果为 true,则数组公式是动态的(如果适用)                        |
    | `r` | 富文本编码(如果适用)                                            |
    | `h` | 富文本的 HTML 渲染(如果适用)                                     |
    | `c` | 与单元格关联的注释                                                |
    | `z` | 与单元格关联的数字格式字符串(如果已请求)                          |
    | `l` | 单元格超链接对象(`.Target` 保存链接,`.Tooltip` 是工具提示)      |
    | `s` | 单元格的样式/主题(如果适用)                                      |
    
    内置的导出工具(例如 CSV 导出器)将使用 `w` 文本(如果可用)。要更改值,请务必在尝试导出之前删除 `cell.w`(或将其设置为 `undefined`)。如果可能,这些工具将根据数字格式(`cell.z`)和原始值重新生成 `w` 文本。
    
    实际数组公式存储在数组范围中第一个单元格的 `f` 字段中。该范围中的其他单元格将省略 `f` 字段。
    
    #### 数据类型
    
    原始值存储在 `v` 值属性中,并根据 `t` 类型属性进行解释。这种分离允许表示数字以及数字文本。有 6 种有效的单元格类型:
    
    | 类型 | 描述                                                           |
    | :--: | :------------------------------------------------------------ |
    | `b`  | 布尔值:值解释为 JS `boolean`                                 |
    | `e`  | 错误:值是数字代码,`w` 属性存储通用名称 **                   |
    | `n`  | 数字:值是 JS `number` **                                      |
    | `d`  | 日期:值是 JS `Date` 对象或要解析为日期的字符串 **              |
    | `s`  | 文本:值解释为 JS `string` 并作为文本写入 **                    |
    | `z`  | 存根:空白存根单元格,数据处理工具忽略 **                      |
    
    <details>
      <summary><b>错误值和解释</b>(点击显示)</summary>
    
    |  值    | 错误含义        |
    | -----: | :-------------- |
    | `0x00` | `#NULL!`        |
    | `0x07` | `#DIV/0!`       |
    | `0x0F` | `#VALUE!`       |
    | `0x17` | `#REF!`         |
    | `0x1D` | `#NAME?`        |
    | `0x24` | `#NUM!`         |
    | `0x2A` | `#N/A`          |
    | `0x2B` | `#GETTING_DATA` |
    
    </details>
    
    类型 `n` 是数字类型。这包括 Excel 存储为数字的所有数据形式,例如日期/时间和布尔字段。Excel 仅使用可适应 IEEE754 浮点数的数据,就像 JS Number 一样,因此 `v` 字段保存原始数字。`w` 字段保存格式化文本。日期默认存储为数字,并使用 `XLSX.SSF.parse_date_code` 进行转换。
    
    类型 `d` 是日期类型,仅当传递 `cellDates` 选项时生成。由于 JSON 没有原生的日期类型,解析器通常期望存储 ISO 8601 日期字符串,就像从 `date.toISOString()` 得到的那样。另一方面,写入器和导出器应能够处理日期字符串和 JS Date 对象。请注意,Excel 忽略时区修饰符,并将所有日期视为本地时区。库不会纠正此错误。
    
    类型 `s` 是字符串类型。值明确存储为文本。Excel 会将这些单元格解释为“存储为文本的数字”。生成的 Excel 文件会自动抑制此类错误,但其他格式可能会引发错误。
    
    类型 `z` 表示空白存根单元格。当单元格没有赋值但包含注释或其他元数据时生成。核心库数据处理工具函数会忽略这些单元格。默认情况下不生成这些单元格;必须将解析器 `sheetStubs` 选项设置为 `true`。
    
    #### 日期
    
    <details>
      <summary><b>Excel 日期代码详细信息</b>(点击显示)</summary>
    
    默认情况下,Excel 将日期存储为具有指定日期处理格式代码的数字。例如,日期 `19-Feb-17` 存储为数字 `42785`,数字格式为 `d-mmm-yy`。`SSF` 模块理解数字格式并执行适当的转换。
    
    XLSX 还支持特殊的日期类型 `d`,其中数据是 ISO 8601 日期字符串。格式化程序将日期转换回数字。
    
    所有解析器的默认行为是生成数字单元格。将 `cellDates` 设置为 true 将强制生成器存储日期。
    
    </details>
    
    <details>
      <summary><b>时区和日期</b>(点击显示)</summary>
    
    Excel 没有原生的通用时间概念。所有时间均在本地时区指定。Excel 的限制阻止指定真正的绝对日期。
    
    遵循 Excel,此库将所有日期视为相对于本地时区。
    
    </details>
    
    <details>
      <summary><b>纪元:1900 和 1904</b>(点击显示)</summary>
    
    Excel 支持两个纪元(1900 年 1 月 1 日和 1904 年 1 月 1 日)。
    可以通过检查工作簿的 `wb.Workbook.WBProps.date1904` 属性来确定工作簿的纪元:```js
    !!(((wb.Workbook||{}).WBProps||{}).date1904)
    ```
    </details>
    
    ### 工作表对象
    
    每个不以 `!` 开头的键都映射到一个单元格(使用 `A-1` 表示法)
    
    `sheet[address]` 返回指定地址的单元格对象。
    
    **特殊工作表键(以 `sheet[key]` 访问,每个键都以 `!` 开头):**
    
    - `sheet['!ref']`:表示工作表范围的基于 A-1 的区域。处理工作表的函数应使用此参数来确定范围。分配到区域之外的单元格不会被处理。特别地,当手动编写工作表时,区域外的单元格不会被包含在内
    
      处理工作表的函数应检查 `!ref` 字段是否存在。如果省略了 `!ref` 或它不是有效的区域,函数可以自由地将工作表视为空或尝试猜测区域。该库附带的标准实用程序将工作表视为空(例如,CSV 输出为空字符串)。
    
      当读取设置了 `sheetRows` 属性的工作表时,ref 参数将使用受限区域。原始区域设置于 `ws['!fullref']`
    
    - `sheet['!margins']`:表示页面边距的对象。默认值遵循 Excel 的 'normal' 预设。Excel 还有 'wide' 和 'narrow' 预设,但它们以原始测量值存储。主要属性如下:
    
    <details>
      <summary><b>页面边距详情</b>(点击展开)</summary>
    
    | 键        | 描述                | "normal" | "wide" | "narrow" |
    |-----------|---------------------|:---------|:-------|:-------- |
    | `left`    | 左边距(英寸)       | `0.7`    | `1.0`  | `0.25`   |
    | `right`   | 右边距(英寸)       | `0.7`    | `1.0`  | `0.25`   |
    | `top`     | 上边距(英寸)       | `0.75`   | `1.0`  | `0.75`   |
    | `bottom`  | 下边距(英寸)       | `0.75`   | `1.0`  | `0.75`   |
    | `header`  | 页眉边距(英寸)     | `0.3`    | `0.5`  | `0.3`    |
    | `footer`  | 页脚边距(英寸)     | `0.3`    | `0.5`  | `0.3`    |```js
    /* Set worksheet sheet to "normal" */
    ws["!margins"]={left:0.7, right:0.7, top:0.75,bottom:0.75,header:0.3,footer:0.3}
    /* Set worksheet sheet to "wide" */
    ws["!margins"]={left:1.0, right:1.0, top:1.0, bottom:1.0, header:0.5,footer:0.5}
    /* Set worksheet sheet to "narrow" */
    ws["!margins"]={left:0.25,right:0.25,top:0.75,bottom:0.75,header:0.3,footer:0.3}
    ```
    </details>
    
    #### 工作表对象
    
    除了基本的工作表键之外,工作表还添加了:
    
    - `ws['!cols']`:列属性对象的数组。列宽实际上以规范化方式存储在文件中,以“最大数字宽度”(渲染数字0-9的最大宽度,以像素为单位)来衡量。解析时,列对象在`wpx`字段中存储像素宽度,在`wch`字段中存储字符宽度,在`MDW`字段中存储最大数字宽度。
    
    - `ws['!rows']`:行属性对象的数组,如文档后续所述。每个行对象编码了包括行高和可见性在内的属性。
    
    - `ws['!merges']`:与工作表中合并单元格对应的范围对象数组。纯文本格式不支持合并单元格。CSV导出会写入合并范围内所有存在的单元格,因此请确保只设置该范围中的第一个单元格(左上角)。
    
    - `ws['!outline']`:配置大纲的行为方式。选项默认为Excel 2019中的默认设置:
    
    | key       | Excel 功能                                 | 默认值 |
    |:----------|:-------------------------------------------|:-------|
    | `above`   | 取消选中“明细数据下方汇总行”               | `false` |
    | `left`    | 取消选中“明细数据右侧汇总行”               | `false` |
    
    - `ws['!protect']`:写入工作表保护属性的对象。`password`键指定支持密码保护的工作表格式(XLSX/XLSB/XLS)的密码。写入器使用XOR混淆方法。以下键控制工作表保护——设置为`false`以在锁定工作表时启用某个功能,或设置为`true`以禁用某个功能:
    
    <details>
      <summary><b>工作表保护详情</b>(点击展开)</summary>
    
    | key                   | 功能(true=禁用 / false=启用) | 默认值    |
    |:----------------------|:-------------------------------|:----------|
    | `selectLockedCells`   | 选择锁定单元格                 | 已启用    |
    | `selectUnlockedCells` | 选择未锁定单元格               | 已启用    |
    | `formatCells`         | 设置单元格格式                 | 已禁用    |
    | `formatColumns`       | 设置列格式                     | 已禁用    |
    | `formatRows`          | 设置行格式                     | 已禁用    |
    | `insertColumns`       | 插入列                         | 已禁用    |
    | `insertRows`          | 插入行                         | 已禁用    |
    | `insertHyperlinks`    | 插入超链接                     | 已禁用    |
    | `deleteColumns`       | 删除列                         | 已禁用    |
    | `deleteRows`          | 删除行                         | 已禁用    |
    | `sort`                | 排序                           | 已禁用    |
    | `autoFilter`          | 筛选                           | 已禁用    |
    | `pivotTables`         | 使用数据透视表报表             | 已禁用    |
    | `objects`             | 编辑对象                       | 已启用    |
    | `scenarios`           | 编辑方案                       | 已启用    |
    </details>
    
    - `ws['!autofilter']`:AutoFilter对象,遵循以下模式:```typescript
    type AutoFilter = {
      ref:string; // A-1 based range representing the AutoFilter table range
    }
    ```
    #### 图表对象
    
    图表以标准工作表形式表示。它们通过将 `!type` 属性设置为 `"chart"` 来区分。
    
    底层数据和 `!ref` 引用图表工作表中的缓存数据。图表工作表的第一行是底层表头。
    
    #### 宏对象
    
    宏以标准工作表形式表示。它们通过将 `!type` 属性设置为 `"macro"` 来区分。
    
    #### 对话框对象
    
    对话框以标准工作表形式表示。它们通过将 `!type` 属性设置为 `"dialog"` 来区分。
    
    ### 工作簿对象
    
    `workbook.SheetNames` 是工作簿中工作表的排序列表。
    
    `wb.Sheets[sheetname]` 返回表示工作表对象的对象。
    
    `wb.Props` 是存储标准属性的对象。 `wb.Custprops` 存储自定义属性。由于 XLS 标准属性与 XLSX 标准不同,XLS 解析将核心属性存储在这两个位置。
    
    `wb.Workbook` 存储[工作簿级属性](#workbook-level-attributes)。
    
    #### 工作簿文件属性
    
    各种文件格式使用不同的文件属性内部名称。工作簿的 `Props` 对象对这些名称进行标准化:
    
    <details>
      <summary><b>文件属性</b>(点击展开)</summary>
    
    | JS 名称       | Excel 描述                    |
    |:--------------|:-------------------------------|
    | `Title`       | 摘要选项卡“标题”              |
    | `Subject`     | 摘要选项卡“主题”              |
    | `Author`      | 摘要选项卡“作者”              |
    | `Manager`     | 摘要选项卡“经理”              |
    | `Company`     | 摘要选项卡“公司”              |
    | `Category`    | 摘要选项卡“类别”              |
    | `Keywords`    | 摘要选项卡“关键词”            |
    | `Comments`    | 摘要选项卡“备注”              |
    | `LastAuthor`  | 统计选项卡“最后保存者”        |
    | `CreatedDate` | 统计选项卡“创建日期”          |
    
    </details>
    
    例如,要设置工作簿标题属性:```js
    if(!wb.Props) wb.Props = {};
    wb.Props.Title = "Insert Title Here";
    ```
    自定义属性被添加到工作簿的 `Custprops` 对象中:```js
    if(!wb.Custprops) wb.Custprops = {};
    wb.Custprops["Custom Property"] = "Custom Value";
    ```
    编写者将处理选项对象的 `Props` 键:```js
    /* force the Author to be "SheetJS" */
    XLSX.write(wb, {Props:{Author:"SheetJS"}});
    ```
    ### 工作簿级属性
    
    `wb.Workbook` 存储工作簿级属性。
    
    #### 已定义名称
    
    `wb.Workbook.Names` 是一个已定义名称对象的数组,这些对象具有以下键:
    
    <details>
      <summary><b>已定义名称属性</b>(点击展开)</summary>
    
    | 键         | 描述                                                           |
    |:-----------|:---------------------------------------------------------------|
    | `Sheet`    | 名称范围。工作表索引(0 表示第一个工作表)或 `null`(工作簿)     |
    | `Name`     | 区分大小写的名称。适用标准规则**                               |
    | `Ref`      | A1 样式引用(`"Sheet1!$A$1:$D$20"`)                           |
    | `Comment`  | 注释(仅适用于 XLS/XLSX/XLSB 格式)                            |
    
    </details>
    
    Excel 允许两个工作表范围的已定义名称共享同一个名称。但是,工作表范围的名称不能与工作簿范围的名称冲突。工作簿写入器可能不强制执行此约束。
    
    #### 工作簿视图
    
    `wb.Workbook.Views` 是一个工作簿视图对象的数组,这些对象具有以下键:
    
    | 键    | 描述                     |
    |:------|:-------------------------|
    | `RTL` | 如果为 true,则从右向左显示 |
    
    #### 其他工作簿属性
    
    `wb.Workbook.WBProps` 保存其他工作簿属性:
    
    | 键              | 描述                                                     |
    |:----------------|:---------------------------------------------------------|
    | `CodeName`      | [VBA 项目工作簿代码名称](#vba-and-macros)                |
    | `date1904`      | 纪元:0/假表示 1900 系统,1/真表示 1904 系统             |
    | `filterPrivacy` | 保存时警告或去除个人身份信息                             |
    
    ### 文档特性
    
    即使对于日期存储等基本功能,官方 Excel 格式也以不同方式存储相同内容。解析器应能将底层文件格式表示转换为公共电子表格格式(CSF)。写入器应能将 CSF 转换回底层文件格式。
    
    #### 公式
    
    A1 样式公式字符串存储在 `f` 字段中。尽管不同文件格式以不同方式存储公式,但格式会被转换。尽管某些格式在公式前加上等号,CSF 公式不以 `=` 开头。
    
    <details>
      <summary><b>公式文件格式支持</b>(点击展开)</summary>
    
    | 存储表示         | 格式                     | 读取 | 写入 |
    |:-----------------|:-------------------------|:----:|:----:|
    | A1 样式字符串    | XLSX                     |  ✔   |  ✔   |
    | RC 样式字符串    | XLML 和纯文本            |  ✔   |  ✔   |
    | BIFF 解析公式    | XLSB 及所有 XLS 格式     |  ✔   |      |
    | OpenFormula 公式 | ODS/FODS/UOS             |  ✔   |  ✔   |
    | Lotus 解析公式   | 所有 Lotus WK_ 格式      |  ✔   |      |
    
    由于 Excel 禁止命名单元格与 A1 或 RC 样式单元格引用名称冲突,因此可以通过(不太简单的)正则表达式进行转换。BIFF 解析公式和 Lotus 解析公式必须显式展开。OpenFormula 公式可以通过正则表达式转换。
    
    共享公式会被解压缩,每个单元格包含对应其位置的公式。写入器通常不尝试生成共享公式。
    </details>
    
    **单单元格公式**
    
    对于简单公式,目标单元格的 `f` 键可以设置为实际公式文本。以下工作表表示 `A1=1`、`A2=2` 以及 `A3=A1+A2`:```js
    var worksheet = {
      "!ref": "A1:A3",
      A1: { t:'n', v:1 },
      A2: { t:'n', v:2 },
      A3: { t:'n', v:3, f:'A1+A2' }
    };
    ```
    如 `aoa_to_sheet` 之类的工具将接受单元格对象而非值:```js
    var worksheet = XLSX.utils.aoa_to_sheet([
      [ 1 ], // A1
      [ 2 ], // A2
      [ {t: "n", v: 3, f: "A1+A2"} ] // A3
    ]);
    ```
    包含公式条目但没有值的单元格将以Excel和其他电子表格工具能够识别的方式进行序列化。此库不会自动计算公式结果!例如,以下工作表将包含`BESSELJ`函数,但该结果在JavaScript中不可用:```js
    var worksheet = XLSX.utils.aoa_to_sheet([
      [ 3.14159, 2 ], // Row "1"
      [ { t:'n', f:'BESSELJ(A1,B1)' } ] // Row "2" will be calculated on file open
    }
    ```
    如果实际结果需要在 JS 中获取,[SheetJS Pro](https://sheetjs.com/pro) 提供了一个公式计算组件,用于评估表达式、更新值和依赖单元格,以及刷新整个工作簿。
    
    **数组公式**
    
    _分配一个数组公式_```js
    XLSX.utils.sheet_set_array_formula(worksheet, range, formula);
    ```
    数组公式存储在数组块的左上角单元格中。所有单元格
    的数组公式都有一个对应于范围的 `F` 字段。单单元格
    公式可以通过是否存在 `F` 字段与普通公式区分。
    
    例如,将单元格 `C1` 设置为数组公式 `{=SUM(A1:A3*B1:B3)}`:```js
    // API function
    XLSX.utils.sheet_set_array_formula(worksheet, "C1", "SUM(A1:A3*B1:B3)");
    
    // ... OR raw operations
    worksheet['C1'] = { t:'n', f: "SUM(A1:A3*B1:B3)", F:"C1:C1" };
    ```
    对于多单元格数组公式,每个单元格具有相同的数组范围,但只有第一个单元格指定公式。考虑 `D1:D3=A1:A3*B1:B3`:```js
    // API function
    XLSX.utils.sheet_set_array_formula(worksheet, "D1:D3", "A1:A3*B1:B3");
    
    // ... OR raw operations
    worksheet['D1'] = { t:'n', F:"D1:D3", f:"A1:A3*B1:B3" };
    worksheet['D2'] = { t:'n', F:"D1:D3" };
    worksheet['D3'] = { t:'n', F:"D1:D3" };
    ```
    实用工具和编写器应检查是否存在 `F` 字段,并忽略起始单元格以外其他单元格中可能存在的公式元素 `f`。它们不需要对公式进行验证!
    
    
    **动态数组公式**
    
    _分配动态数组公式_```js
    XLSX.utils.sheet_set_array_formula(worksheet, range, formula, true);
    ```
    Released in 2020, Dynamic Array Formulae are supported in the XLSX/XLSM and XLSB
    file formats.  They are represented like normal array formulae but have special
    cell metadata indicating that the formula should be allowed to adjust the range.
    
    An array formula can be marked as dynamic by setting the cell's `D` property to
    true.  The `F` range is expected but can be the set to the current cell:```js
    // API function
    XLSX.utils.sheet_set_array_formula(worksheet, "C1", "_xlfn.UNIQUE(A1:A3)", 1);
    
    // ... OR raw operations
    worksheet['C1'] = { t: "s", f: "_xlfn.UNIQUE(A1:A3)", F:"C1", D: 1 }; // dynamic
    ```
    **函数名称的本地化**
    
    SheetJS 在文件级别操作。Excel 使用英文(美国)函数名称存储公式表达式。对于非英语用户,Excel 使用一组本地化的函数名称。
    
    例如,当计算机语言和区域设置为法语(法国)时,Excel 会将 `=SOMME(A1:C3)` 解释为 `SOMME` 是 `SUM` 函数。然而,在实际文件中,Excel 存储的是 `SUM(A1:C3)`。
    
    **前缀为“未来函数”**
    
    在较新版本的 Excel 中引入的函数在存储到文件中时,会添加 `_xlfn.` 前缀。在使用这些函数编写公式表达式时,为了最大兼容性,需要加上该前缀:```js
    // Broadest compatibility
    XLSX.utils.sheet_set_array_formula(worksheet, "C1", "_xlfn.UNIQUE(A1:A3)", 1);
    
    // Can cause errors in spreadsheet software
    XLSX.utils.sheet_set_array_formula(worksheet, "C1", "UNIQUE(A1:A3)", 1);
    ```
    When reading a file, the `xlfn` option preserves the prefixes.
    
    <details>
      <summary><b> 需要 `_xlfn.` 前缀的函数</b> (点击展开)</summary>
    
    这个列表随着每个 Excel 版本的发布而不断增加。```
    ACOT
    ACOTH
    AGGREGATE
    ARABIC
    BASE
    BETA.DIST
    BETA.INV
    BINOM.DIST
    BINOM.DIST.RANGE
    BINOM.INV
    BITAND
    BITLSHIFT
    BITOR
    BITRSHIFT
    BITXOR
    BYCOL
    BYROW
    CEILING.MATH
    CEILING.PRECISE
    CHISQ.DIST
    CHISQ.DIST.RT
    CHISQ.INV
    CHISQ.INV.RT
    CHISQ.TEST
    COMBINA
    CONFIDENCE.NORM
    CONFIDENCE.T
    COT
    COTH
    COVARIANCE.P
    COVARIANCE.S
    CSC
    CSCH
    DAYS
    DECIMAL
    ERF.PRECISE
    ERFC.PRECISE
    EXPON.DIST
    F.DIST
    F.DIST.RT
    F.INV
    F.INV.RT
    F.TEST
    FIELDVALUE
    FILTERXML
    FLOOR.MATH
    FLOOR.PRECISE
    FORMULATEXT
    GAMMA
    GAMMA.DIST
    GAMMA.INV
    GAMMALN.PRECISE
    GAUSS
    HYPGEOM.DIST
    IFNA
    IMCOSH
    IMCOT
    IMCSC
    IMCSCH
    IMSEC
    IMSECH
    IMSINH
    IMTAN
    ISFORMULA
    ISOMITTED
    ISOWEEKNUM
    LAMBDA
    LET
    LOGNORM.DIST
    LOGNORM.INV
    MAKEARRAY
    MAP
    MODE.MULT
    MODE.SNGL
    MUNIT
    NEGBINOM.DIST
    NORM.DIST
    NORM.INV
    NORM.S.DIST
    NORM.S.INV
    NUMBERVALUE
    PDURATION
    PERCENTILE.EXC
    PERCENTILE.INC
    PERCENTRANK.EXC
    PERCENTRANK.INC
    PERMUTATIONA
    PHI
    POISSON.DIST
    QUARTILE.EXC
    QUARTILE.INC
    QUERYSTRING
    RANDARRAY
    RANK.AVG
    RANK.EQ
    REDUCE
    RRI
    SCAN
    SEC
    SECH
    SEQUENCE
    SHEET
    SHEETS
    SKEW.P
    SORTBY
    STDEV.P
    STDEV.S
    T.DIST
    T.DIST.2T
    T.DIST.RT
    T.INV
    T.INV.2T
    T.TEST
    UNICHAR
    UNICODE
    UNIQUE
    VAR.P
    VAR.S
    WEBSERVICE
    WEIBULL.DIST
    XLOOKUP
    XOR
    Z.TEST
    ```
    </details>
    
    #### 行和列属性
    
    <details>
      <summary><b>格式支持</b>(点击显示)</summary>
    
    **行属性**:XLSX/M, XLSB, BIFF8 XLS, XLML, SYLK, DOM, ODS
    
    **列属性**:XLSX/M, XLSB, BIFF8 XLS, XLML, SYLK, DOM
    
    </details>
    
    
    默认情况下,读取文件时不会提取行和列属性,写入文件时也不会保留它们。必须将选项 `cellStyles: true` 传递给相关的读取或写入函数。
    
    _列属性_
    
    每个工作表中存在的 `!cols` 数组(如果存在)是一个 `ColInfo` 对象的集合,这些对象具有以下属性:```typescript
    type ColInfo = {
      /* visibility */
      hidden?: boolean; // if true, the column is hidden
    
      /* column width is specified in one of the following ways: */
      wpx?:    number;  // width in screen pixels
      width?:  number;  // width in Excel's "Max Digit Width", width*256 is integral
      wch?:    number;  // width in characters
    
      /* other fields for preserving features from files */
      level?:  number;  // 0-indexed outline / group level
      MDW?:    number;  // Excel's "Max Digit Width" unit, always integral
    };
    ```
    _Row Properties_
    
    每个工作表中的 `!rows` 数组(如果存在)是一个 `RowInfo` 对象的集合,具有以下属性:```typescript
    type RowInfo = {
      /* visibility */
      hidden?: boolean; // if true, the row is hidden
    
      /* row height is specified in one of the following ways: */
      hpx?:    number;  // height in screen pixels
      hpt?:    number;  // height in points
    
      level?:  number;  // 0-indexed outline / group level
    };
    ```
    _大纲 / 分组级别约定_
    
    Excel UI 将基础大纲级别显示为 `1`,最大级别显示为 `8`。根据 JS 惯例,SheetJS 使用从 0 开始索引的大纲级别,其中基础大纲级别为 `0`,最大级别为 `7`。
    
    <details>
      <summary><b>为什么有三种宽度类型?</b>(点击显示)</summary>
    
    有三种不同的宽度类型,对应电子表格存储列宽的三种不同方式:
    
    SYLK 和其他纯文本格式使用原始字符计数。同时代的工具如 Visicalc 和 Multiplan 是基于字符的。由于字符宽度相同,存储计数就足够了。这一传统延续到了 BIFF 格式中。
    
    SpreadsheetML (2003) 试图通过在整个文件中标准化屏幕像素计数来与 HTML 对齐。列宽、行高和其他度量使用像素。当像素和字符计数不一致时,Excel 会四舍五入。
    
    XLSX 内部以模糊的“最大数字宽度 (Max Digit Width)”形式存储列宽。最大数字宽度是渲染时最大数字的宽度(通常字符“0”最宽)。内部宽度必须为宽度除以 256 的整数倍。ECMA-376 描述了像素与内部宽度之间的转换公式。这代表了一种混合方法。
    
    读取函数尝试填充所有三个属性。写入函数会尝试将指定值循环转换为所需类型。为避免潜在冲突,操作应首先删除其他属性。例如,更改像素宽度时,删除 `wch` 和 `width` 属性。
    </details>
    
    <details>
      <summary><b>实现细节</b>(点击显示)</summary>
    
    _行高_
    
    Excel 内部以点为单位存储行高。默认分辨率为 72 DPI 或 96 PPI,因此像素和点的大小应该一致。对于不同的分辨率,它们可能不一致,因此库将这两个概念分开。
    
    尽管所有信息都已提供,但写入器应遵循优先级顺序:
    
    1) 如果可用,使用 `hpx` 像素高度
    2) 如果可用,使用 `hpt` 点高度
    
    _列宽_
    
    给定约束条件下,可以在不实际检查字体的情况下确定 MDW!解析器通过将宽度转换为像素再转换回来,对所有可能的 MDW 进行重复,并选择误差最小的 MDW 来猜测像素宽度。XLML 实际存储了像素宽度,因此猜测方向相反。
    
    尽管所有信息都已提供,但写入器应遵循优先级顺序:
    
    1) 如果可用,使用 `width` 字段
    2) 如果可用,使用 `wpx` 像素宽度
    3) 如果可用,使用 `wch` 字符计数
    
    </details>
    
    #### 数字格式
    
    每个单元格的 `cell.w` 格式化文本由 `cell.v` 和 `cell.z` 格式生成。如果未指定格式,则使用 Excel 的 `General` 格式。格式可以指定为字符串,也可以指定为格式表中的索引。解析器应使用数字格式表填充 `workbook.SSF`。写入器应序列化该表。
    
    自定义工具应确保本地表中包含每个使用的格式字符串。Excel 约定要求自定义格式从索引 164 开始。以下示例从头开始创建自定义格式:
    
    <details>
      <summary><b>使用自定义格式的新工作表</b>(点击显示)</summary>```js
    var wb = {
      SheetNames: ["Sheet1"],
      Sheets: {
        Sheet1: {
          "!ref":"A1:C1",
          A1: { t:"n", v:10000 },                    // <-- General format
          B1: { t:"n", v:10000, z: "0%" },           // <-- Builtin format
          C1: { t:"n", v:10000, z: "\"T\"\ #0.00" }  // <-- Custom format
        }
      }
    }
    ```
    </details>
    
    规则与 Excel 显示自定义数字格式的方式略有不同。特别是,字面字符必须用双引号括起来或前面加上反斜杠。有关更多信息,请参阅 Excel 文档文章 `Create or delete a custom number format` 或 ECMA-376 18.8.31 (Number Formats)
    
    
    <details>
      <summary><b>默认数字格式</b> (点击展开)</summary>
    
    默认格式列于 ECMA-376 18.8.30:
    
    | ID | 格式                     |
    |---:|:---------------------------|
    |  0 | `General`                  |
    |  1 | `0`                        |
    |  2 | `0.00`                     |
    |  3 | `#,##0`                    |
    |  4 | `#,##0.00`                 |
    |  9 | `0%`                       |
    | 10 | `0.00%`                    |
    | 11 | `0.00E+00`                 |
    | 12 | `# ?/?`                    |
    | 13 | `# ??/??`                  |
    | 14 | `m/d/yy` (见下文)          |
    | 15 | `d-mmm-yy`                 |
    | 16 | `d-mmm`                    |
    | 17 | `mmm-yy`                   |
    | 18 | `h:mm AM/PM`               |
    | 19 | `h:mm:ss AM/PM`            |
    | 20 | `h:mm`                     |
    | 21 | `h:mm:ss`                  |
    | 22 | `m/d/yy h:mm`              |
    | 37 | `#,##0 ;(#,##0)`           |
    | 38 | `#,##0 ;[Red](#,##0)`      |
    | 39 | `#,##0.00;(#,##0.00)`      |
    | 40 | `#,##0.00;[Red](#,##0.00)` |
    | 45 | `mm:ss`                    |
    | 46 | `[h]:mm:ss`                |
    | 47 | `mmss.0`                   |
    | 48 | `##0.0E+0`                 |
    | 49 | `@`                        |
    
    </details>
    
    格式 14(`m/d/yy`)由 Excel 本地化:即使文件指定了该数字格式,它也会根据系统设置以不同方式显示。当文件的生产者和消费者处于相同区域设置时,这很合理,但在互联网上并非总是如此。为了解决这种歧义,解析函数接受 `dateNF` 选项以覆盖对该特定格式字符串的解释。
    
    #### 超链接
    
    <details>
      <summary><b>格式支持</b> (点击展开)</summary>
    
    **单元格超链接**: XLSX/M, XLSB, BIFF8 XLS, XLML, ODS
    
    **工具提示**: XLSX/M, XLSB, BIFF8 XLS, XLML
    
    </details>
    
    超链接存储在单元格对象的 `l` 键中。超链接对象的 `Target` 字段是链接的目标,包括 URI 片段。工具提示存储在 `Tooltip` 字段中,当您将鼠标移到文本上时显示。
    
    例如,以下代码片段创建了一个从单元格 `A3` 到 <https://sheetjs.com> 的链接,并带有提示 `"Find us @ SheetJS.com!"`:```js
    ws['A1'].l = { Target:"https://sheetjs.com", Tooltip:"Find us @ SheetJS.com!" };
    ```
    请注意,Excel不会自动为超链接添加样式——它们通常会以普通文本形式显示。
    
    _远程链接_
    
    HTTP/HTTPS 链接可直接使用:```js
    ws['A2'].l = { Target:"https://docs.sheetjs.com/#hyperlinks" };
    ws['A3'].l = { Target:"http://localhost:7262/yes_localhost_works" };
    ```
    Excel也支持带主题行的`mailto`邮件链接:```js
    ws['A4'].l = { Target:"mailto:[email protected]" };
    ws['A5'].l = { Target:"mailto:[email protected]?subject=Test Subject" };
    ```
    _本地链接_
    
    指向绝对路径的链接应使用 `file://` URI 方案:```js
    ws['B1'].l = { Target:"file:///SheetJS/t.xlsx" }; /* Link to /SheetJS/t.xlsx */
    ws['B2'].l = { Target:"file:///c:/SheetJS.xlsx" }; /* Link to c:\SheetJS.xlsx */
    ```
    相对路径的链接可以不指定协议(scheme)进行指定:```js
    ws['B3'].l = { Target:"SheetJS.xlsb" }; /* Link to SheetJS.xlsb */
    ws['B4'].l = { Target:"../SheetJS.xlsm" }; /* Link to ../SheetJS.xlsm */
    ```
    相对路径在 SpreadsheetML 2003 格式中行为未定义。Excel 2019 会将 `..\` 父级标记视为向上两级。
    
    _内部链接_
    
    目标为同一工作簿中的单元格、区域或已定义名称的链接(“内部链接”)以井号字符开头:```js
    ws['C1'].l = { Target:"#E2" }; /* Link to cell E2 */
    ws['C2'].l = { Target:"#Sheet2!E2" }; /* Link to cell E2 in sheet Sheet2 */
    ws['C3'].l = { Target:"#SomeDefinedName" }; /* Link to Defined Name */
    ```
    #### 单元格注释
    
    单元格注释是存储在单元格对象的`c`数组中的对象。注释的实际内容会根据评论作者拆分为多个块。每个注释对象的`a`字段是评论作者,`t`字段是纯文本表示。
    
    例如,以下代码片段将一个单元格注释追加到单元格`A1`:```js
    if(!ws.A1.c) ws.A1.c = [];
    ws.A1.c.push({a:"SheetJS", t:"I'm a little comment, short and stout!"});
    ```
    注意:XLSB 对作者姓名字符数限制为 54 个字符。超过 54 个字符的名称可能会导致其他格式出现问题。
    
    要将评论标记为默认隐藏,请设置 `hidden` 属性:```js
    if(!ws.A1.c) ws.A1.c = [];
    ws.A1.c.push({a:"SheetJS", t:"This comment is visible"});
    
    if(!ws.A2.c) ws.A2.c = [];
    ws.A2.c.hidden = true;
    ws.A2.c.push({a:"SheetJS", t:"This comment will be hidden"});
    ```
    _线程评论_
    
    在 Excel 365 中引入,线程评论是带有作者元数据和父引用的纯文本评论片段,
    支持在 XLSX 和 XLSB 中使用。
    
    要将评论标记为线程,每个评论部分必须有一个为真的 `T` 属性:```js
    if(!ws.A1.c) ws.A1.c = [];
    ws.A1.c.push({a:"SheetJS", t:"This is not threaded"});
    
    if(!ws.A2.c) ws.A2.c = [];
    ws.A2.c.hidden = true;
    ws.A2.c.push({a:"SheetJS", t:"This is threaded", T: true});
    ws.A2.c.push({a:"JSSheet", t:"This is also threaded", T: true});
    ```
    在线程中,作者的元数据与 Active Directory 或 Office 365 无关。
    
    #### 工作表可见性
    
    Excel 允许在底部标签栏中隐藏工作表。工作表数据存储在文件中,但用户界面并不容易直接访问。标准隐藏的工作表可以通过“取消隐藏”菜单显示。Excel 还有“深度隐藏”的工作表,无法通过菜单显示。只能在 VB 编辑器中访问!
    
    可见性设置存储在工作表属性数组的 `Hidden` 属性中。
    
    <details>
      <summary><b>更多详情</b> (点击展开)</summary>
    
    | 值   | 定义     |
    |:----:|:---------|
    |  0   | 可见     |
    |  1   | 隐藏     |
    |  2   | 深度隐藏 |
    
    使用 <https://rawgit.com/SheetJS/test_files/HEAD/sheet_visibility.xlsx>:```js
    > wb.Workbook.Sheets.map(function(x) { return [x.name, x.Hidden] })
    [ [ 'Visible', 0 ], [ 'Hidden', 1 ], [ 'VeryHidden', 2 ] ]
    ```
    非Excel格式不支持“非常隐藏”状态。测试工作表是否可见的最佳方法是检查 `Hidden` 属性是否为逻辑真值:```js
    > wb.Workbook.Sheets.map(function(x) { return [x.name, !x.Hidden] })
    [ [ 'Visible', true ], [ 'Hidden', false ], [ 'VeryHidden', false ] ]
    ```
    </details>
    
    #### VBA 与宏
    
    VBA 宏存储在特殊的数据 blob 中,当 `bookVBA` 选项为 `true` 时,通过工作簿对象的 `vbaraw` 属性公开。它们在 `XLSM`、`XLSB` 和 `BIFF8 XLS` 格式中受支持。如果数据 blob 存在于工作簿中,支持的格式写入器会自动插入它,并与工作表名称关联。
    
    <details>
    	<summary><b>自定义代码名称</b>(点击显示)</summary>
    
    工作簿代码名称存储在 `wb.Workbook.WBProps.CodeName` 中。默认情况下,Excel 会写入 `ThisWorkbook` 或翻译后的短语,如 `DieseArbeitsmappe`。工作表和图表表的代码名称位于工作表属性对象 `wb.Workbook.Sheets[i].CodeName` 中。宏表和对话框表被忽略。
    
    读取器和写入器保留代码名称,但在将 VBA blob 添加到不同工作簿时需要手动设置。
    
    </details>
    
    <details>
    	<summary><b>宏表</b>(点击显示)</summary>
    
    较旧版本的 Excel 还支持一种非 VBA 的"macrosheet"工作表类型,用于存储自动化命令。这些通过对象的 `!type` 属性设置为 `"macro"` 来公开。
    
    </details>
    
    <details>
    	<summary><b>检测工作簿中的宏</b>(点击显示)</summary>
    
    仅当宏存在时,`vbaraw` 字段才会被设置,因此测试很简单:
    
    ```
    if (wb.vbaraw) { /* macros detected */ }
    ``````js
    function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
    	if(!!wb.vbaraw) return true;
    	const sheets = wb.SheetNames.map((n) => wb.Sheets[n]);
    	return sheets.some((ws) => !!ws && ws['!type']=='macro');
    }
    ```
    </details>
    
    ## 解析选项
    
    导出的 `read` 和 `readFile` 函数接受一个选项参数:
    
    | 选项名    | 默认值  | 描述                                              |
    | :-------- | ------: | :------------------------------------------------ |
    | `type`    |         | 输入数据编码(参见下方的 Input Type)               |
    | `raw`     | false   | 若为 true,纯文本解析将不会解析值 **              |
    | `codepage`|         | 若指定,则在适当情况下使用代码页 **               |
    |`cellFormula`| true    | 将公式保存到 .f 字段                              |
    |`cellHTML`   | true    | 解析富文本并将 HTML 保存到 `.h` 字段               |
    |`cellNF`     | false   | 将数字格式字符串保存到 `.z` 字段                   |
    |`cellStyles` | false   | 将样式/主题信息保存到 `.s` 字段                    |
    |`cellText`   | true    | 将生成的格式化文本保存到 `.w` 字段                 |
    |`cellDates`  | false   | 将日期存储为类型 `d`(默认是 `n`)                |
    |`dateNF`     |         | 若指定,则使用该字符串作为日期代码 14 **           |
    |`sheetStubs` | false   | 为占位单元格创建类型为 `z` 的单元格对象            |
    |`sheetRows`  | 0       | 若 >0,只读取前 `sheetRows` 行 **                 |
    |`bookDeps`   | false   | 若为 true,解析计算链                              |
    |`bookFiles`  | false   | 若为 true,将原始文件添加到 book 对象 **           |
    |`bookProps`  | false   | 若为 true,仅解析足够获取 book 元数据 **           |
    |`bookSheets` | false   | 若为 true,仅解析足够获取工作表名称                |
    |`bookVBA`    | false   | 若为 true,将 VBA blob 复制到 `vbaraw` 字段 **     |
    |`password`   | ""      | 若定义且文件已加密,则使用密码 **                  |
    |`WTF`        | false   | 若为 true,在遇到意外的文件特性时抛出错误 **       |
    |`sheets`     |         | 若指定,仅解析指定的工作表 **                      |
    |`PRN`        | false   | 若为 true,允许解析 PRN 文件 **                    |
    |`xlfn`       | false   | 若为 true,保留公式中的 `_xlfn.` 前缀 **           |
    |`FS`         |         | DSV 字段分隔符覆盖                                 |
    
    - 即使 `cellNF` 为 false,也会生成格式化文本并保存到 `.w`
    - 在某些情况下,即使 `bookSheets` 为 false,也可能解析工作表。
    - Excel 会积极地尝试从 CSV 及其他纯文本中解释值。这会导致令人惊讶的行为!`raw` 选项会抑制值解析。
    - `bookSheets` 和 `bookProps` 结合起来可提供两组信息。
    - 若 `bookDeps` 为 false,则 `Deps` 将是一个空对象。
    - `bookFiles` 行为取决于文件类型:
        * 对于基于 ZIP 的格式,`keys` 数组(ZIP 中的路径)
        * 对于 ZIP,`files` 哈希(从路径映射到表示文件的对象的映射)
        * 对于使用 CFB 容器的格式,`cfb` 对象
    - 查看 JSON 对象输出时会生成 `sheetRows-1` 行(因为解析数据时标题行也被计为一行)。
    - 默认情况下解析所有工作表。`sheets` 根据输入类型进行限制:
        * 数字:要解析的工作表的从零开始的索引(`0` 是第一个工作表)
        * 字符串:要解析的工作表的名称(不区分大小写)
        * 数字和字符串的数组,用于选择多个工作表。
    - `bookVBA` 仅暴露原始的 VBA CFB 对象。它不解析数据。XLSM 和 XLSB 将 VBA CFB 对象存储在 `xl/vbaProject.bin` 中。BIFF8 XLS 将 VBA 条目与核心 Workbook 条目混合在一起,因此库会从 XLS CFB 容器生成一个新的兼容 XLSB 的 blob。
    - `codepage` 应用于不带 `CodePage` 记录的 BIFF2 - BIFF5 文件,以及在 `type:"binary"` 中没有 BOM 的 CSV 文件。BIFF8 XLS 始终默认使用 1200。
    - `PRN` 影响没有通用分隔符的文本文件的解析。
    - 目前仅支持 XOR 加密。对于采用其他加密方法的文件,将抛出 Unsupported 错误。
    - 较新的 Excel 函数使用 `_xlfn.` 前缀进行序列化,对用户隐藏。SheetJS 通常会去掉 `_xlfn.`。`xlfn` 选项会保留它们。
    - WTF 主要用于开发。默认情况下,解析器会抑制单个工作表的读取错误,允许您读取能够正确解析的工作表。设置 `WTF:true` 会强制抛出这些错误。
    
    ### 输入类型
    
    字符串可以用多种方式解释。`read` 的 `type` 参数告诉库如何解析数据参数:
    
    | `type`     | 预期输入                                                       |
    |------------|----------------------------------------------------------------|
    | `"base64"` | 字符串:文件内容的 Base64 编码                                  |
    | `"binary"` | 字符串:二进制字符串(字节 `n` 是 `data.charCodeAt(n)`)       |
    | `"string"` | 字符串:JS 字符串(字符解释为 UTF8)                            |
    | `"buffer"` | nodejs Buffer                                                   |
    | `"array"`  | 数组:8 位无符号整数数组(字节 `n` 是 `data[n]`)              |
    | `"file"`   | 字符串:将被读取的文件的路径(仅 nodejs)                      |
    
    ### 猜测文件类型
    
    <details>
      <summary><b>实现细节</b>(点击展开)</summary>
    
    Excel 和其他电子表格工具会读取前几个字节并应用其他启发式方法来判断文件类型。这允许文件类型嫁接:将文件重命名为 `.xls` 扩展名会告诉您的计算机使用 Excel 打开该文件,但 Excel 会知道如何处理它。此库应用类似的逻辑:
    
    | 字节 0 | 原始文件类型 | 电子表格类型                                             |
    |:-------|:-------------|:--------------------------------------------------------|
    | `0xD0` | CFB 容器     | BIFF 5/8 或受保护的 XLSX/XLSB 或 WQ3/QPW 或 XLR         |
    | `0x09` | BIFF 流      | BIFF 2/3/4/5                                            |
    | `0x3C` | XML/HTML     | SpreadsheetML / Flat ODS / UOS1 / HTML / 纯文本         |
    | `0x50` | ZIP 存档     | XLSB 或 XLSX/M 或 ODS 或 UOS2 或 NUMBERS 或 文本        |
    | `0x49` | 纯文本       | SYLK 或 纯文本                                          |
    | `0x54` | 纯文本       | DIF 或 纯文本                                           |
    | `0xEF` | UTF8 编码    | SpreadsheetML / Flat ODS / UOS1 / HTML / 纯文本         |
    | `0xFF` | UTF16 编码   | SpreadsheetML / Flat ODS / UOS1 / HTML / 纯文本         |
    | `0x00` | 记录流       | Lotus WK\* 或 Quattro Pro 或 纯文本                     |
    | `0x7B` | 纯文本       | RTF 或 纯文本                                           |
    | `0x0A` | 纯文本       | SpreadsheetML / Flat ODS / UOS1 / HTML / 纯文本         |
    | `0x0D` | 纯文本       | SpreadsheetML / Flat ODS / UOS1 / HTML / 纯文本         |
    | `0x20` | 纯文本       | SpreadsheetML / Flat ODS / UOS1 / HTML / 纯文本         |
    
    DBF 文件根据第一个字节以及第三和第四个字节(对应于文件日期的月份和日期)检测。
    
    Works for Windows 文件根据 BOF 记录(类型为 `0xFF`)检测。
    
    纯文本格式猜测按以下优先级进行:
    
    | 格式 | 测试                                                                    |
    |:-----|:------------------------------------------------------------------------|
    | XML  | 前 1024 个字符中出现 `<?xml`                                             |
    | HTML | 以 `<` 开头且前 1024 个字符中出现 HTML 标签 *                           |
    | XML  | 以 `<` 开头且第一个标签有效                                              |
    | RTF  | 以 `{\rt` 开头                                                           |
    | DSV  | 以 `/sep=.$/` 开头,分隔符为指定字符                                    |
    | DSV  | 前 1024 个字符中未引用的 `|` 字符多于 `;`、`\t`、`,`                      |
    | DSV  | 前 1024 个字符中未引用的 `;` 字符多于 `\t` 或 `,`                        |
    | TSV  | 前 1024 个字符中未引用的 `\t` 字符多于 `,` 字符                          |
    | CSV  | 前 1024 个字符中有一个逗号 `","`                                         |
    | ETH  | 以 `socialcalc:version:` 开头                                            |
    | PRN  | `PRN` 选项设置为 true                                                    |
    | CSV  | (回退)                                                                 |
    
    - HTML 标签包括:`html`、`table`、`head`、`meta`、`script`、`style`、`div`
    
    </details>
    
    <details>
      <summary><b>为什么随机文本文件也是有效的?</b>(点击展开)</summary>
    
    Excel 在读取文件时非常激进。为任何显示文本文件(其中仅包含 ANSI 显示字符)添加 XLS 扩展名会欺骗 Excel,使其认为该文件可能是 CSV 或 TSV 文件,即使它只有一列!此库尝试模拟这种行为。
    
    最佳方法是验证所需的工作表,并确保它具有预期的行数或列数。提取范围非常简单:```js
    var range = XLSX.utils.decode_range(worksheet['!ref']);
    var ncols = range.e.c - range.s.c + 1, nrows = range.e.r - range.s.r + 1;
    ```
    </details>
    
    ## 写入选项
    
    导出的 `write` 和 `writeFile` 函数接受一个选项参数:
    
    | 选项名      | 默认值   | 描述                                         |
    | :---------- | -------: | :-------------------------------------------------- |
    |`type`       |          | 输出数据编码(详见下方输出类型)        |
    |`cellDates`  |  `false` | 将日期存储为类型 `d`(默认为 `n`)            |
    |`bookSST`    |  `false` | 生成共享字符串表 **                     |
    |`bookType`   | `"xlsx"` | 工作簿类型(详见下方支持的格式)  |
    |`sheet`      |     `""` | 单表格式的工作表名称 **       |
    |`compression`|  `false` | 对基于 ZIP 的格式使用 ZIP 压缩 **        |
    |`Props`      |          | 写入时覆盖工作簿属性 **        |
    |`themeXLSX`  |          | 写入 XLSX/XLSB/XLSM 时覆盖主题 XML **   |
    |`ignoreEC`   |   `true` | 抑制“数字视为文本”错误 **                 |
    |`numbers`    |          | 导出 NUMBERS 格式的数据 **                       |
    
    - `bookSST` 更慢且更占用内存,但与旧版 iOS Numbers 有更好的兼容性
    - 仅保证保存原始数据。本 README 未描述的功能可能不会被序列化。
    - `cellDates` 仅适用于 XLSX 输出,且不保证与第三方读取器兼容。Excel 本身通常不写入类型为 `d` 的单元格,因此非 Excel 工具可能会忽略数据或在存在日期时出错。
    - `Props` 是一个镜像工作簿 `Props` 字段的对象。详见[工作簿文件属性](#workbook-file-properties)部分的表格。
    - 如果指定,`themeXLSX` 中的字符串将作为 XLSX/XLSB/XLSM 文件的主主题保存(位于 ZIP 中的 `xl/theme/theme1.xml`)。
    - 由于程序漏洞,某些功能(如“文本分列”)会在忽略错误条件的工作表上导致 Excel 崩溃。写入器默认会标记文件忽略该错误。将 `ignoreEC` 设为 `false` 以取消。
    - 由于数据量较大,NUMBERS 数据默认不包含在内。包含的 `xlsx.zahl.js` 和 `xlsx.zahl.mjs` 脚本包含了该数据。
    
    ### 支持的输出格式
    
    为确保与第三方工具的广泛兼容性,本库支持多种输出格式。具体文件类型由 `bookType` 选项控制:
    
    | `bookType` | 文件扩展名 | 容器   | 表数量 | 描述                     |
    | :--------- | -------: | :-------: | :----- |:------------------------------- |
    | `xlsx`     | `.xlsx`  |    ZIP    | 多表  | Excel 2007+ XML 格式          |
    | `xlsm`     | `.xlsm`  |    ZIP    | 多表  | Excel 2007+ 宏 XML 格式    |
    | `xlsb`     | `.xlsb`  |    ZIP    | 多表  | Excel 2007+ 二进制格式       |
    | `biff8`    | `.xls`   |    CFB    | 多表  | Excel 97-2004 工作簿格式   |
    | `biff5`    | `.xls`   |    CFB    | 多表  | Excel 5.0/95 工作簿格式    |
    | `biff4`    | `.xls`   |   none    | 单表 | Excel 4.0 工作表格式      |
    | `biff3`    | `.xls`   |   none    | 单表 | Excel 3.0 工作表格式      |
    | `biff2`    | `.xls`   |   none    | 单表 | Excel 2.0 工作表格式      |
    | `xlml`     | `.xls`   |   none    | 多表  | Excel 2003-2004 (SpreadsheetML) |
    | `numbers`  |`.numbers`|    ZIP    | 单表 | Numbers 3.0+ 电子表格        |
    | `ods`      | `.ods`   |    ZIP    | 多表  | OpenDocument 电子表格        |
    | `fods`     | `.fods`  |   none    | 多表  | 扁平 OpenDocument 电子表格   |
    | `wk3`      | `.wk3`   |   none    | 多表  | Lotus 工作簿 (WK3)            |
    | `csv`      | `.csv`   |   none    | 单表 | 逗号分隔值          |
    | `txt`      | `.txt`   |   none    | 单表 | UTF-16 Unicode 文本 (TXT)       |
    | `sylk`     | `.sylk`  |   none    | 单表 | 符号链接 (SYLK)            |
    | `html`     | `.html`  |   none    | 单表 | HTML 文档                   |
    | `dif`      | `.dif`   |   none    | 单表 | 数据交换格式 (DIF)   |
    | `dbf`      | `.dbf`   |   none    | 单表 | dBASE II + VFP 扩展 (DBF) |
    | `wk1`      | `.wk1`   |   none    | 单表 | Lotus 工作表 (WK1)           |
    | `rtf`      | `.rtf`   |   none    | 单表 | 富文本格式 (RTF)          |
    | `prn`      | `.prn`   |   none    | 单表 | Lotus 格式化文本            |
    | `eth`      | `.eth`   |   none    | 单表 | Ethercalc 记录格式 (ETH)   |
    
    - `compression` 仅适用于 ZIP 容器格式。
    - 仅支持单表的格式需要指定 `sheet` 选项来指明工作表。如果字符串为空,则使用第一个工作表。
    - 如果未指定 `bookType`,`writeFile` 将根据文件扩展名自动猜测输出文件格式。它将选择上述表格中第一个与扩展名匹配的格式。
    
    ### 输出类型
    
    `write` 的 `type` 参数与 `read` 的 `type` 参数对应:
    
    | `type`     | 输出                                                          |
    |------------|-----------------------------------------------------------------|
    | `"base64"` | 字符串:文件的 Base64 编码                             |
    | `"binary"` | 字符串:二进制字符串(字节 `n` 为 `data.charCodeAt(n)`)        |
    | `"string"` | 字符串:JS 字符串(字符按 UTF8 解释)              |
    | `"buffer"` | nodejs Buffer                                                   |
    | `"array"`  | ArrayBuffer,8 位无符号整数的后备数组               |
    | `"file"`   | 字符串:将要创建的文件的路径(仅限 nodejs)         |
    
    - 为与 Excel 兼容,`csv` 输出将始终包含 UTF-8 字节顺序标记。
    
    ## 工具函数
    
    `sheet_to_*` 函数接受一个工作表和一个可选的选项对象。
    
    `*_to_sheet` 函数接受一个数据对象和一个可选的选项对象。
    
    示例基于以下工作表:```
    XXX| A | B | C | D | E | F | G |
    ---+---+---+---+---+---+---+---+
     1 | S | h | e | e | t | J | S |
     2 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
     3 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    ```
    ### 数组的数组输入
    
    `XLSX.utils.aoa_to_sheet` 接受一个由 JS 值组成的数组的数组,并返回一个类似输入数据的工作表。数字、布尔值和字符串将存储为相应的样式。日期存储为日期或数字。数组空洞和显式的 `undefined` 值将被跳过。`null` 值可能会被桩化。所有其他值都将存储为字符串。该函数接受一个选项参数:
    
    | 选项名称      | 默认值  | 描述                                                |
    | :---------- | :-----: | :--------------------------------------------------- |
    |`dateNF`     |  FMT 14 | 在字符串输出中使用指定的日期格式                           |
    |`cellDates`  |  false  | 将日期存储为类型 `d`(默认为 `n`)                         |
    |`sheetStubs` |  false  | 为 `null` 值创建类型 `z` 的单元格对象                      |
    |`nullError`  |  false  | 如果为 true,则为 `null` 值生成 `#NULL!` 错误单元格         |
    
    <details>
      <summary><b>示例</b> (点击展开)</summary>
    
    要生成示例工作表:```js
    var ws = XLSX.utils.aoa_to_sheet([
      "SheetJS".split(""),
      [1,2,3,4,5,6,7],
      [2,3,4,5,6,7,8]
    ]);
    ```
    </details>
    
    `XLSX.utils.sheet_add_aoa` 接受一个由 JS 值组成的数组的数组,并更新现有的工作表对象。它遵循与 `aoa_to_sheet` 相同的过程,并接受一个选项参数:
    
    | 选项名称   | 默认值  | 描述                                        |
    | :--------- | :-----: | :------------------------------------------ |
    |`dateNF`    |  FMT 14 | 在字符串输出中使用指定的日期格式            |
    |`cellDates` |  false  | 将日期存储为类型 `d`(默认是 `n`)          |
    |`sheetStubs`|  false  | 为 `null` 值创建类型为 `z` 的单元格对象     |
    |`nullError` |  false  | 如果为 true,则为 `null` 值生成 `#NULL!` 错误单元格 |
    |`origin`    |         | 使用指定的单元格作为起点(见下文)          |
    
    `origin` 应为以下之一:
    
    | `origin`         | 描述                                                   |
    | :--------------- | :----------------------------------------------------- |
    | (单元格对象)     | 使用指定的单元格(单元格对象)                          |
    | (字符串)         | 使用指定的单元格(A1 样式单元格)                      |
    | (数字 >= 0)      | 从指定行的第一列开始(从 0 开始索引)                  |
    | -1               | 从第一列开始附加到工作表底部                            |
    | (默认)           | 从单元格 A1 开始                                        |
    
    <details>
      <summary><b>示例</b>(点击展开)</summary>
    
    考虑以下工作表:```
    XXX| A | B | C | D | E | F | G |
    ---+---+---+---+---+---+---+---+
     1 | S | h | e | e | t | J | S |
     2 | 1 | 2 |   |   | 5 | 6 | 7 |
     3 | 2 | 3 |   |   | 6 | 7 | 8 |
     4 | 3 | 4 |   |   | 7 | 8 | 9 |
     5 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
    ```
    这个工作表可以按以下顺序构建:`A1:G1, A2:B4, E2:G4, A5:G5`:```js
    /* Initial row */
    var ws = XLSX.utils.aoa_to_sheet([ "SheetJS".split("") ]);
    
    /* Write data starting at A2 */
    XLSX.utils.sheet_add_aoa(ws, [[1,2], [2,3], [3,4]], {origin: "A2"});
    
    /* Write data starting at E2 */
    XLSX.utils.sheet_add_aoa(ws, [[5,6,7], [6,7,8], [7,8,9]], {origin:{r:1, c:4}});
    
    /* Append row */
    XLSX.utils.sheet_add_aoa(ws, [[4,5,6,7,8,9,0]], {origin: -1});
    ```
    </details>
    
    ### 对象数组输入
    
    `XLSX.utils.json_to_sheet` 接受对象数组,并根据对象的键自动生成“标题”来返回工作表。默认列顺序由使用 `Object.keys` 首次出现的字段决定。该函数接受一个选项参数:
    
    | 选项名称   | 默认值  | 描述                                                 |
    | :--------- | :-----: | :--------------------------------------------------- |
    | `header`   |         | 使用指定的字段顺序(默认 `Object.keys`)**            |
    | `dateNF`   |  FMT 14 | 在字符串输出中使用指定的日期格式                     |
    | `cellDates`|  false  | 以类型 `d` 存储日期(默认为 `n`)                    |
    | `skipHeader`| false  | 若为 true,输出中不包含标题行                        |
    | `nullError`| false  | 若为 true,为 `null` 值生成 `#NULL!` 错误单元格      |
    
    - 每行的所有字段都会被写入。如果 `header` 是数组且未包含某个特定字段,则该键会被追加到数组中。
    - 单元格类型根据每个值的类型推断。例如,`Date` 对象将生成日期单元格,而字符串将生成文本单元格。
    - 默认情况下会跳过空值。如果 `nullError` 为 true,则会在工作表中写入与 `#NULL!` 对应的错误单元格。
    
    <details>
      <summary><b>示例</b>(点击展开)</summary>
    
    由于 JavaScript 对象键必须是唯一的,因此无法直接使用普通对象重现原始工作表。将第二个 `e` 和 `S` 替换为 `e_1` 和 `S_1` 后:```js
    var ws = XLSX.utils.json_to_sheet([
      { S:1, h:2, e:3, e_1:4, t:5, J:6, S_1:7 },
      { S:2, h:3, e:4, e_1:5, t:6, J:7, S_1:8 }
    ], {header:["S","h","e","e_1","t","J","S_1"]});
    ```
    或者,可以跳过表头行:```js
    var ws = XLSX.utils.json_to_sheet([
      { A:"S", B:"h", C:"e", D:"e", E:"t", F:"J", G:"S" },
      { A: 1,  B: 2,  C: 3,  D: 4,  E: 5,  F: 6,  G: 7  },
      { A: 2,  B: 3,  C: 4,  D: 5,  E: 6,  F: 7,  G: 8  }
    ], {header:["A","B","C","D","E","F","G"], skipHeader:true});
    ```
    </details>
    
    `XLSX.utils.sheet_add_json` 接受一个对象数组并更新现有工作表对象。其过程与 `json_to_sheet` 相同,并接受一个选项参数:
    
    | 选项名称     | 默认值 | 描述                                                 |
    | :---------- | :----: | :--------------------------------------------------- |
    |`header`     |        | 使用指定的列顺序(默认 `Object.keys`)               |
    |`dateNF`     | FMT 14 | 在字符串输出中使用指定的日期格式                     |
    |`cellDates`  | false  | 将日期存储为 `d` 类型(默认是 `n`)                  |
    |`skipHeader` | false  | 如果为 true,则输出中不包含标题行                     |
    |`nullError`  | false  | 如果为 true,为 `null` 值生成 `#NULL!` 错误单元格    |
    |`origin`     |        | 使用指定的单元格作为起始点(见下文)                 |
    
    `origin` 应为以下之一:
    
    | `origin`         | 描述                                                   |
    | :--------------- | :------------------------------------------------------ |
    | (单元格对象)     | 使用指定的单元格(单元格对象)                           |
    | (字符串)         | 使用指定的单元格(A1 样式单元格)                       |
    | (数字 >= 0)      | 从指定行(0 索引)的第一列开始                          |
    | -1               | 追加到工作表底部,从第一列开始                          |
    | (默认)           | 从单元格 A1 开始                                        |
    
    
    <details>
      <summary><b>示例</b>(点击展开)</summary>
    
    考虑以下工作表:```
    XXX| A | B | C | D | E | F | G |
    ---+---+---+---+---+---+---+---+
     1 | S | h | e | e | t | J | S |
     2 | 1 | 2 |   |   | 5 | 6 | 7 |
     3 | 2 | 3 |   |   | 6 | 7 | 8 |
     4 | 3 | 4 |   |   | 7 | 8 | 9 |
     5 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
    ```
    这个工作表可以按顺序 `A1:G1, A2:B4, E2:G4, A5:G5` 构建:```js
    /* Initial row */
    var ws = XLSX.utils.json_to_sheet([
      { A: "S", B: "h", C: "e", D: "e", E: "t", F: "J", G: "S" }
    ], {header: ["A", "B", "C", "D", "E", "F", "G"], skipHeader: true});
    
    /* Write data starting at A2 */
    XLSX.utils.sheet_add_json(ws, [
      { A: 1, B: 2 }, { A: 2, B: 3 }, { A: 3, B: 4 }
    ], {skipHeader: true, origin: "A2"});
    
    /* Write data starting at E2 */
    XLSX.utils.sheet_add_json(ws, [
      { A: 5, B: 6, C: 7 }, { A: 6, B: 7, C: 8 }, { A: 7, B: 8, C: 9 }
    ], {skipHeader: true, origin: { r: 1, c: 4 }, header: [ "A", "B", "C" ]});
    
    /* Append row */
    XLSX.utils.sheet_add_json(ws, [
      { A: 4, B: 5, C: 6, D: 7, E: 8, F: 9, G: 0 }
    ], {header: ["A", "B", "C", "D", "E", "F", "G"], skipHeader: true, origin: -1});
    ```
    </details>
    
    ### HTML 表格输入
    
    `XLSX.utils.table_to_sheet` 接受一个表格 DOM 元素,并返回一个与输入表格类似的工作表。数字会被解析。所有其他数据将作为字符串存储。
    
    `XLSX.utils.table_to_book` 基于该工作表生成一个最小工作簿。
    
    这两个函数都接受选项参数:
    
    | 选项名称 |  默认值  | 描述                                              |
    | :------- | :------: | :-------------------------------------------------- |
    |`raw`     |          | 如果为 true,每个单元格将包含原始字符串            |
    |`dateNF`  |  FMT 14  | 在字符串输出中使用指定的日期格式                    |
    |`cellDates`|  false   | 将日期存储为类型 `d`(默认是 `n`)                   |
    |`sheetRows`|    0     | 如果 >0,读取表格的前 `sheetRows` 行                |
    |`display` |  false   | 如果为 true,将不解析隐藏的行和单元格                |
    
    <details>
      <summary><b>示例</b>(点击展开)</summary>
    
    要生成示例工作表,请从以下 HTML 表格开始:```html
    <table id="sheetjs">
    <tr><td>S</td><td>h</td><td>e</td><td>e</td><td>t</td><td>J</td><td>S</td></tr>
    <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td></tr>
    <tr><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td></tr>
    </table>
    ```
    要处理该表格:```js
    var tbl = document.getElementById('sheetjs');
    var wb = XLSX.utils.table_to_book(tbl);
    ```
    </details>
    
    注意:`XLSX.read` 可以处理以字符串形式呈现的 HTML。
    
    `XLSX.utils.sheet_add_dom` 接收一个表格 DOM 元素,并更新已有的工作表对象。其处理过程与 `table_to_sheet` 相同,并接受一个选项参数:
    
    | 选项名称   |  默认值  | 描述                                           |
    | :--------- | :------: | :--------------------------------------------- |
    | `raw`      |          | 若为 true,每个单元格将保留原始字符串          |
    | `dateNF`   |  FMT 14  | 在字符串输出中使用指定的日期格式               |
    | `cellDates`|  false   | 将日期存储为类型`d`(默认为 `n`)              |
    | `sheetRows`|    0     | 若 >0,则读取表格的前 `sheetRows` 行          |
    | `display`  |  false   | 若为 true,将不解析隐藏的行和单元格            |
    
    `origin` 应为以下值之一:
    
    | `origin`          | 描述                                              |
    | :---------------- | :------------------------------------------------ |
    | (单元格对象)   | 使用指定的单元格(单元格对象)                    |
    | (字符串)       | 使用指定的单元格(A1 样式单元格)                 |
    | (数值 >= 0)    | 从指定行的第一列开始(从 0 开始索引)             |
    | -1                | 从第一列开始,追加到工作表底部                    |
    
    ---
    
    [阅读更多](https://github.com/weareu/xlsx)