forked from sheetjs/sheetjs
README cleanup in anticipation of node fetch
This commit is contained in:
parent
3cbe83c855
commit
6c436ae277
@ -148,6 +148,7 @@ storages
|
||||
|
||||
- demos/extendscript/README.md
|
||||
Photoshop
|
||||
InDesign
|
||||
minifier
|
||||
|
||||
- demos/function/README.md
|
||||
|
@ -1,21 +1,21 @@
|
||||
var CT_VBA = "application/vnd.ms-office.vbaProject";
|
||||
function make_vba_xls(cfb/*:CFBContainer*/) {
|
||||
var newcfb = CFB.utils.cfb_new({root:"R"});
|
||||
cfb.FullPaths.forEach(function(p, i) {
|
||||
if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
|
||||
var newpath = p.replace(/^[^\/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
|
||||
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
|
||||
});
|
||||
return CFB.write(newcfb);
|
||||
function make_vba_xls(cfb) {
|
||||
var newcfb = CFB.utils.cfb_new({ root: "R" });
|
||||
cfb.FullPaths.forEach(function(p, i) {
|
||||
if (p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/))
|
||||
return;
|
||||
var newpath = p.replace(/^[^\/]*/, "R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
|
||||
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
|
||||
});
|
||||
return CFB.write(newcfb);
|
||||
}
|
||||
|
||||
function fill_vba_xls(cfb/*:CFBContainer*/, vba/*:CFBContainer*/)/*:void*/ {
|
||||
vba.FullPaths.forEach(function(p, i) {
|
||||
if(i == 0) return;
|
||||
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
|
||||
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
|
||||
});
|
||||
function fill_vba_xls(cfb, vba) {
|
||||
vba.FullPaths.forEach(function(p, i) {
|
||||
if (i == 0)
|
||||
return;
|
||||
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
|
||||
if (newpath.slice(-1) !== "/")
|
||||
CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
|
||||
});
|
||||
}
|
||||
|
||||
var VBAFMTS = [ "xlsb", "xlsm", "xlam", "biff8", "xla" ];
|
||||
|
||||
var VBAFMTS = ["xlsb", "xlsm", "xlam", "biff8", "xla"];
|
||||
|
@ -869,7 +869,8 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
||||
nameidx = (f[1][2]/*:any*/);
|
||||
var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx];
|
||||
var name = lbl ? lbl.Name : "SH33TJSNAME" + String(nameidx);
|
||||
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
|
||||
/* [MS-XLSB] 2.5.97.10 Ftab -- last verified 20220204 */
|
||||
if(name && name.slice(0,6) == "_xlfn.") name = name.slice(6);
|
||||
stack.push(name);
|
||||
break;
|
||||
|
||||
|
2393
bits/64_ftab.js
2393
bits/64_ftab.js
File diff suppressed because it is too large
Load Diff
0
bits/83_numbers.js
Executable file → Normal file
0
bits/83_numbers.js
Executable file → Normal file
@ -7,7 +7,7 @@ document.addEventListener('mousedown', function(mouse) {
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
|
||||
if(!msg && !msg['Sheet']) return;
|
||||
if(!msg || !msg['Sheet']) return;
|
||||
if(msg.Sheet == "JS") {
|
||||
var elt = document.elementFromPoint(coords[0], coords[1]);
|
||||
while(elt != null) {
|
||||
|
@ -1,10 +1,33 @@
|
||||
# Internet Explorer
|
||||
|
||||
Despite the efforts to deprecate the pertinent operating systems, IE is still
|
||||
very popular, required for various government and corporate websites throughout
|
||||
the world. The modern upload and download strategies are not available in older
|
||||
versions of IE, but there are alternative approaches.
|
||||
The modern upload and download strategies are not available in older versions of
|
||||
IE, but there are approaches using older technologies like ActiveX and Flash.
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demos</b> (click to show)</summary>
|
||||
|
||||
<http://oss.sheetjs.com/sheetjs/ajax.html> uses XMLHttpRequest to download test
|
||||
files and convert to CSV.
|
||||
|
||||
<https://oss.sheetjs.com/sheetjs/> demonstrates reading files with `FileReader`.
|
||||
|
||||
Older versions of IE do not support HTML5 File API but do support Base64.
|
||||
|
||||
On OSX you can get the Base64 encoding with:
|
||||
|
||||
```bash
|
||||
$ <target_file base64 | pbcopy
|
||||
```
|
||||
|
||||
On Windows XP and up you can get the Base64 encoding using `certutil`:
|
||||
|
||||
```cmd
|
||||
> certutil -encode target_file target_file.b64
|
||||
```
|
||||
|
||||
(note: You have to open the file and remove the header and footer lines)
|
||||
|
||||
</details>
|
||||
|
||||
## Upload Strategies
|
||||
|
||||
@ -14,14 +37,14 @@ IE10 and IE11 support the standard HTML5 FileReader API:
|
||||
|
||||
```js
|
||||
function handle_fr(e) {
|
||||
var files = e.target.files, f = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var data = new Uint8Array(e.target.result);
|
||||
var wb = XLSX.read(data, {type: 'array'});
|
||||
process_wb(wb);
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
var files = e.target.files, f = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var data = new Uint8Array(e.target.result);
|
||||
var wb = XLSX.read(data, {type: 'array'});
|
||||
process_wb(wb);
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
}
|
||||
input_dom_element.addEventListener('change', handle_fr, false);
|
||||
```
|
||||
@ -36,12 +59,12 @@ should be called from a file input `onchange` event:
|
||||
```js
|
||||
var input_dom_element = document.getElementById("file");
|
||||
function handle_ie() {
|
||||
/* get data from selected file */
|
||||
var path = input_dom_element.value;
|
||||
var bstr = IE_LoadFile(path);
|
||||
/* read workbook */
|
||||
var wb = XLSX.read(bstr, {type: 'binary'});
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
/* get data from selected file */
|
||||
var path = input_dom_element.value;
|
||||
var bstr = IE_LoadFile(path);
|
||||
/* read workbook */
|
||||
var wb = XLSX.read(bstr, {type: 'binary'});
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
}
|
||||
input_dom_element.attachEvent('onchange', handle_ie);
|
||||
```
|
||||
@ -105,10 +128,10 @@ to a Base64 string and passing to the library:
|
||||
|
||||
```js
|
||||
Downloadify.create(element_id, {
|
||||
/* the demo includes the other options required by Downloadify */
|
||||
filename: "test.xlsx",
|
||||
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
|
||||
dataType: 'base64'
|
||||
/* the demo includes the other options required by Downloadify */
|
||||
filename: "test.xlsx",
|
||||
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
|
||||
dataType: 'base64'
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -13,7 +13,7 @@ The library can also be imported directly from JSX code with:
|
||||
import XLSX from 'xlsx';
|
||||
```
|
||||
|
||||
This demo shows a simple React component transpiled in the browser using the babel
|
||||
This demo shows a simple React component transpiled in the browser using Babel
|
||||
standalone library. Since there is no standard React table model, this demo
|
||||
settles on the array of arrays approach.
|
||||
|
||||
@ -77,45 +77,29 @@ function make_cols(refstr/*:string*/) {
|
||||
Reproducing the full project is straightforward:
|
||||
|
||||
```bash
|
||||
# see native.sh
|
||||
react-native init SheetJS
|
||||
cd SheetJS
|
||||
npm i -S xlsx react react-native react-native-table-component react-native-fs
|
||||
cp ../react-native.js index.js
|
||||
react-native link
|
||||
$ make native # build the project
|
||||
$ make ios # build and run the iOS demo
|
||||
$ make android # build and run the android demo
|
||||
```
|
||||
|
||||
`react-native-table-component` draws the data table. `react-native-fs` reads
|
||||
and write files on devices. The app will prompt before reading and after
|
||||
writing data. The printed location will be:
|
||||
The app will prompt before reading and after writing data. The printed location
|
||||
depends on the environment:
|
||||
|
||||
- android: path in the device filesystem
|
||||
- iOS simulator: local path to file
|
||||
- iOS device: a path accessible from iTunes App Documents view
|
||||
|
||||
`react-native-fs` supports `"ascii"` encoding for `readFile` and `writeFile`.
|
||||
In practice, that encoding uses binary strings compatible with `"binary"` type:
|
||||
Components used in the demo:
|
||||
- [`react-native-table-component`](https://npm.im/react-native-table-component)
|
||||
- [`react-native-file-access`](https://npm.im/react-native-file-access)
|
||||
|
||||
```js
|
||||
import { writeFile, readFile } from 'react-native-fs';
|
||||
|
||||
/* read a workbook */
|
||||
readFile(file, 'ascii').then((res) => {
|
||||
const workbook = XLSX.read(res, {type:'binary'});
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
});
|
||||
|
||||
/* write a workbook */
|
||||
const wbout = XLSX.write(wb, {type:'binary', bookType:"xlsx"});
|
||||
writeFile(file, wbout, 'ascii').then((r)=>{/* :) */}).catch((e)=>{/* :( */});
|
||||
```
|
||||
React Native does not provide a native component for reading and writing files.
|
||||
The sample script <`react-native.js`> uses `react-native-file-access` and has
|
||||
notes for integrations with `react-native-fetch-blob` and `react-native-fs`.
|
||||
|
||||
Note: for real app deployments, the `UIFileSharingEnabled` flag must be manually
|
||||
set in the iOS project `Info.plist` file.
|
||||
|
||||
To run the React Native demo, run either `make ios` or `make android` while
|
||||
connected to a device or emulator.
|
||||
|
||||
#### Server-Rendered React Components with Next.js
|
||||
|
||||
The demo uses the same component code as the in-browser version, but the build
|
||||
|
@ -1,11 +1,17 @@
|
||||
#!/bin/bash
|
||||
# xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
if [ ! -e SheetJS ]; then
|
||||
react-native init --version="0.62.2" SheetJS
|
||||
cd SheetJS
|
||||
npm i -S xlsx react-native-table-component react-native-fs
|
||||
cd -
|
||||
fi
|
||||
|
||||
# Create starter project
|
||||
if [ ! -e SheetJS ]; then react-native init SheetJS --version="0.67.2"; fi
|
||||
|
||||
# Install dependencies
|
||||
cd SheetJS; npm i -S xlsx react-native-table-component; cd -
|
||||
|
||||
cd SheetJS; npm i -S react-native-file-access@2.x; cd -
|
||||
# cd SheetJS; npm i -S react-native-fs; cd -
|
||||
# cd SheetJS; npm i -S react-native-fetch-blob; cd -
|
||||
|
||||
# Copy demo assets
|
||||
if [ ! -e SheetJS/logo.png ]; then
|
||||
curl -O http://oss.sheetjs.com/assets/img/logo.png
|
||||
mv logo.png SheetJS/logo.png
|
||||
@ -16,6 +22,6 @@ if [ -e SheetJS/index.ios.js ]; then
|
||||
else
|
||||
cp react-native.js SheetJS/index.js
|
||||
fi
|
||||
cd SheetJS;
|
||||
RNFB_ANDROID_PERMISSIONS=true react-native link
|
||||
cd -;
|
||||
|
||||
# Link
|
||||
cd SheetJS; RNFB_ANDROID_PERMISSIONS=true react-native link; cd -
|
||||
|
61
demos/react/react-native.js
vendored
61
demos/react/react-native.js
vendored
@ -14,12 +14,69 @@ import {
|
||||
} from 'react-native';
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
||||
|
||||
// react-native-file-access
|
||||
var Base64 = function() {
|
||||
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
return {
|
||||
encode: function(input) {
|
||||
var o = "";
|
||||
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
|
||||
for (var i = 0; i < input.length; ) {
|
||||
c1 = input.charCodeAt(i++);
|
||||
e1 = c1 >> 2;
|
||||
c2 = input.charCodeAt(i++);
|
||||
e2 = (c1 & 3) << 4 | c2 >> 4;
|
||||
c3 = input.charCodeAt(i++);
|
||||
e3 = (c2 & 15) << 2 | c3 >> 6;
|
||||
e4 = c3 & 63;
|
||||
if (isNaN(c2)) {
|
||||
e3 = e4 = 64;
|
||||
} else if (isNaN(c3)) {
|
||||
e4 = 64;
|
||||
}
|
||||
o += map.charAt(e1) + map.charAt(e2) + map.charAt(e3) + map.charAt(e4);
|
||||
}
|
||||
return o;
|
||||
},
|
||||
decode: function(input) {
|
||||
var o = "";
|
||||
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
|
||||
input = input.replace(/[^\w\+\/\=]/g, "");
|
||||
for (var i = 0; i < input.length; ) {
|
||||
e1 = map.indexOf(input.charAt(i++));
|
||||
e2 = map.indexOf(input.charAt(i++));
|
||||
c1 = e1 << 2 | e2 >> 4;
|
||||
o += String.fromCharCode(c1);
|
||||
e3 = map.indexOf(input.charAt(i++));
|
||||
c2 = (e2 & 15) << 4 | e3 >> 2;
|
||||
if (e3 !== 64) {
|
||||
o += String.fromCharCode(c2);
|
||||
}
|
||||
e4 = map.indexOf(input.charAt(i++));
|
||||
c3 = (e3 & 3) << 6 | e4;
|
||||
if (e4 !== 64) {
|
||||
o += String.fromCharCode(c3);
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
};
|
||||
}();
|
||||
|
||||
import { Dirs, FileSystem } from 'react-native-file-access';
|
||||
const DDP = Dirs.DocumentDir + "/";
|
||||
const readFile = (path, enc) => FileSystem.readFile(path, "base64");
|
||||
const writeFile = (path, data, enc) => FileSystem.writeFile(path, data, "base64");
|
||||
const input = res => Base64.decode(res);
|
||||
const output = str => Base64.encode(str);
|
||||
|
||||
// react-native-fs
|
||||
/*
|
||||
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs';
|
||||
const DDP = DocumentDirectoryPath + "/";
|
||||
const input = res => res;
|
||||
const output = str => str;
|
||||
|
||||
*/
|
||||
// react-native-fetch-blob
|
||||
/*
|
||||
import RNFetchBlob from 'react-native-fetch-blob';
|
||||
@ -36,7 +93,7 @@ export default class SheetJS extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: [[1,2,3],[4,5,6]],
|
||||
data: [[2,3,4],[3,4,5]],
|
||||
widthArr: [60, 60, 60],
|
||||
cols: make_cols("A1:C2")
|
||||
};
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
### Installation
|
||||
|
||||
In the browser, just add a script tag:
|
||||
The complete browser standalone build is saved to `dist/xlsx.full.min.js` and
|
||||
can be directly added to a page with a `script` tag:
|
||||
|
||||
```html
|
||||
<script lang="javascript" src="dist/xlsx.full.min.js"></script>
|
||||
@ -18,7 +19,7 @@ In the browser, just add a script tag:
|
||||
| `CDNjs` | <https://cdnjs.com/libraries/xlsx> |
|
||||
| `packd` | <https://bundle.run/xlsx@latest?name=XLSX> |
|
||||
|
||||
`unpkg` makes the latest version available at:
|
||||
For example, `unpkg` makes the latest version available at:
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
|
||||
@ -26,33 +27,8 @@ In the browser, just add a script tag:
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
With [npm](https://www.npmjs.org/package/xlsx):
|
||||
|
||||
```bash
|
||||
$ npm install xlsx
|
||||
```
|
||||
|
||||
With [bower](https://bower.io/search/?q=js-xlsx):
|
||||
|
||||
```bash
|
||||
$ bower install js-xlsx
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Optional features</b> (click to show)</summary>
|
||||
|
||||
The node version automatically requires modules for additional features. Some
|
||||
of these modules are rather large in size and are only needed in special
|
||||
circumstances, so they do not ship with the core. For browser use, they must
|
||||
be included directly:
|
||||
|
||||
```html
|
||||
<!-- international support from js-codepage -->
|
||||
<script src="dist/cpexcel.js"></script>
|
||||
```
|
||||
|
||||
An appropriate version for each dependency is included in the dist/ directory.
|
||||
<summary><b>Browser builds</b> (click to show)</summary>
|
||||
|
||||
The complete single-file version is generated at `dist/xlsx.full.min.js`
|
||||
|
||||
@ -74,8 +50,30 @@ be configured to remove support with `resolve.alias`:
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
With [npm](https://www.npmjs.org/package/xlsx):
|
||||
|
||||
```bash
|
||||
$ npm install xlsx
|
||||
```
|
||||
|
||||
With [bower](https://bower.io/search/?q=js-xlsx):
|
||||
|
||||
```bash
|
||||
$ bower install js-xlsx
|
||||
```
|
||||
|
||||
`dist/xlsx.extendscript.js` is an ExtendScript build for Photoshop and InDesign
|
||||
that is included in the `npm` package. It can be directly referenced with a
|
||||
`#include` directive:
|
||||
|
||||
```extendscript
|
||||
#include "xlsx.extendscript.js"
|
||||
```
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>ECMAScript 3 Compatibility</b> (click to show)</summary>
|
||||
<summary><b>Internet Explorer and ECMAScript 3 Compatibility</b> (click to show)</summary>
|
||||
|
||||
For broad compatibility with JavaScript engines, the library is written using
|
||||
ECMAScript 3 language dialect as well as some ES5 features like `Array#forEach`.
|
||||
|
@ -31,8 +31,8 @@ var table_elt = document.getElementById("my-table-id");
|
||||
var workbook = XLSX.utils.table_to_book(table_elt);
|
||||
|
||||
// Process Data (add a new row)
|
||||
var worksheet = workbook.Sheets["Sheet1"];
|
||||
XLSX.utils.sheet_add_aoa([["Created "+new Date().toISOString()}]], {origin:-1});
|
||||
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");
|
||||
@ -40,7 +40,8 @@ XLSX.writeFile(workbook, "Report.xlsb");
|
||||
|
||||
This library tries to simplify steps 2 and 4 with functions to extract useful
|
||||
data from spreadsheet files (`read` / `readFile`) and generate new spreadsheet
|
||||
files from data (`write` / `writeFile`).
|
||||
files from data (`write` / `writeFile`). Additional utility functions like
|
||||
`table_to_book` work with other common data sources like HTML tables.
|
||||
|
||||
This documentation and various demo projects cover a number of common scenarios
|
||||
and approaches for steps 1 and 5.
|
||||
@ -48,8 +49,210 @@ and approaches for steps 1 and 5.
|
||||
Utility functions help with step 3.
|
||||
|
||||
|
||||
#### The Zen of SheetJS
|
||||
### The Zen of SheetJS
|
||||
|
||||
_Data processing should fit in any workflow_
|
||||
|
||||
The library does not impose a separate lifecycle. It fits nicely in websites
|
||||
and apps built using any framework. The plain JS data objects play nice with
|
||||
Web Workers and future APIs.
|
||||
|
||||
["Acquiring and Extracting Data"](#acquiring-and-extracting-data) describes
|
||||
solutions for common data import scenarios.
|
||||
|
||||
["Writing Workbooks"](#writing-workbooks) describes solutions for common data
|
||||
export scenarios involving actual spreadsheet files.
|
||||
|
||||
["Utility Functions"](#utility-functions) details utility functions for
|
||||
translating JSON Arrays and other common JS structures into worksheet objects.
|
||||
|
||||
_JavaScript is a powerful language for data processing_
|
||||
|
||||
The ["Common Spreadsheet Format"](#common-spreadsheet-format) is a simple object
|
||||
representation of the core concepts of a workbook. The various functions in the
|
||||
library provide low-level tools for working with the object.
|
||||
|
||||
For friendly JS processing, there are utility functions for converting parts of
|
||||
a worksheet to/from an Array of Arrays. The following example combines powerful
|
||||
JS Array methods with a network request library to download data, select the
|
||||
information we want and create a workbook file:
|
||||
|
||||
<details>
|
||||
<summary><b>Get Data from a JSON Endpoint and Generate a Workbook</b> (click to show)</summary>
|
||||
|
||||
The goal is to generate a XLSB workbook of US President names and birthdays.
|
||||
|
||||
**Acquire Data**
|
||||
|
||||
_Raw Data_
|
||||
|
||||
<https://theunitedstates.io/congress-legislators/executive.json> has the desired
|
||||
data. For example, John Adams:
|
||||
|
||||
```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"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
_Filtering for Presidents_
|
||||
|
||||
The dataset includes Aaron Burr, a Vice President who was never President!
|
||||
|
||||
`Array#filter` creates a new array with the desired rows. A President served
|
||||
at least one term with `type` set to `"prez"`. To test if a particular row has
|
||||
at least one `"prez"` term, `Array#some` is another native JS function. The
|
||||
complete filter would be:
|
||||
|
||||
```js
|
||||
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
|
||||
```
|
||||
|
||||
_Lining up the data_
|
||||
|
||||
For this example, the name will be the first name combined with the last name
|
||||
(`row.name.first + " " + row.name.last`) and the birthday will be the subfield
|
||||
`row.bio.birthday`. Using `Array#map`, the dataset can be massaged in one call:
|
||||
|
||||
```js
|
||||
const rows = prez.map(row => ({
|
||||
name: row.name.first + " " + row.name.last,
|
||||
birthday: row.bio.birthday
|
||||
}));
|
||||
```
|
||||
|
||||
The result is an array of "simple" objects with no nesting:
|
||||
|
||||
```js
|
||||
[
|
||||
{ name: "George Washington", birthday: "1732-02-22" },
|
||||
{ name: "John Adams", birthday: "1735-10-19" },
|
||||
// ... one row per President
|
||||
]
|
||||
```
|
||||
|
||||
**Extract Data**
|
||||
|
||||
With the cleaned dataset, `XLSX.utils.json_to_sheet` generates a worksheet:
|
||||
|
||||
```js
|
||||
const worksheet = XLSX.utils.json_to_sheet(rows);
|
||||
```
|
||||
|
||||
`XLSX.utils.book_new` creates a new workbook and `XLSX.utils.book_append_sheet`
|
||||
appends a worksheet to the workbook. The new worksheet will be called "Dates":
|
||||
|
||||
```js
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
|
||||
```
|
||||
|
||||
**Process Data**
|
||||
|
||||
_Fixing headers_
|
||||
|
||||
By default, `json_to_sheet` creates a worksheet with a header row. In this case,
|
||||
the headers come from the JS object keys: "name" and "birthday".
|
||||
|
||||
The headers are in cells A1 and B1. `XLSX.utils.sheet_add_aoa` can write text
|
||||
values to the existing worksheet starting at cell A1:
|
||||
|
||||
```js
|
||||
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
|
||||
```
|
||||
|
||||
_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
|
||||
```
|
||||
|
||||
One `Array#reduce` call over `rows` can calculate the maximum width:
|
||||
|
||||
```js
|
||||
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
|
||||
worksheet["!cols"] = [ { wch: max_width } ];
|
||||
```
|
||||
|
||||
Note: If the starting point was a file or HTML table, `XLSX.utils.sheet_to_json`
|
||||
will generate an array of JS objects.
|
||||
|
||||
**Package and Release Data**
|
||||
|
||||
`XLSX.writeFile` creates a spreadsheet file and tries to write it to the system.
|
||||
In the browser, it will try to prompt the user to download the file. In NodeJS,
|
||||
it will write to the local directory.
|
||||
|
||||
```js
|
||||
XLSX.writeFile(workbook, "Presidents.xlsx");
|
||||
```
|
||||
|
||||
**Complete Example**
|
||||
|
||||
```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");
|
||||
})();
|
||||
```
|
||||
|
||||
For use in the web browser, assuming the snippet is saved to `snippet.js`,
|
||||
script tags should be used to include the `axios` and `xlsx` standalone builds:
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
_File formats are implementation details_
|
||||
|
||||
@ -59,36 +262,7 @@ The parser covers a wide gamut of common spreadsheet file formats to ensure that
|
||||
The writer supports a number of common output formats for broad compatibility
|
||||
with the data ecosystem.
|
||||
|
||||
|
||||
_Data processing should fit in any workflow_
|
||||
|
||||
The library does not impose a separate lifecycle. It fits nicely in websites
|
||||
and apps built using any framework. The plain JS data objects play nice with
|
||||
Web Workers and future APIs.
|
||||
|
||||
["Parsing Workbooks"](#parsing-workbooks) describes solutions for common data
|
||||
import scenarios involving actual spreadsheet files.
|
||||
|
||||
["Writing Workbooks"](#writing-workbooks) describes solutions for common data
|
||||
export scenarios involving actual spreadsheet files.
|
||||
|
||||
["Utility Functions"](#utility-functions) details utility functions for
|
||||
translating JSON Arrays and other common JS structures into worksheet objects.
|
||||
|
||||
|
||||
_JavaScript is a powerful language for data processing_
|
||||
|
||||
The ["Common Spreadsheet Format"](#common-spreadsheet-format) is a simple object
|
||||
representation of the core concepts of a workbook. The various functions in the
|
||||
library provide low-level tools for working with the object.
|
||||
|
||||
For friendly JS processing, there are utility functions for converting parts of
|
||||
a worksheet to/from an Array of Arrays. For example, summing columns from an
|
||||
array of arrays can be implemented in a single Array reduce operation:
|
||||
|
||||
```js
|
||||
var aoa = XLSX.utils.sheet_to_json(worksheet, {header: 1});
|
||||
var sum_of_column_B = aoa.reduce((acc, row) => acc + (+row[1]||0), 0);
|
||||
```
|
||||
To the greatest extent possible, data processing code should not have to worry
|
||||
about the specific file formats involved.
|
||||
|
||||
|
||||
|
@ -1,82 +1,172 @@
|
||||
## Parsing Workbooks
|
||||
## Acquiring and Extracting Data
|
||||
|
||||
For parsing, the first step is to read the file. This involves acquiring the
|
||||
data and feeding it into the library. Here are a few common scenarios:
|
||||
### Parsing Workbooks
|
||||
|
||||
<details>
|
||||
<summary><b>nodejs read a file</b> (click to show)</summary>
|
||||
#### API
|
||||
|
||||
`readFile` is only available in server environments. Browsers have no API for
|
||||
reading arbitrary files given a path, so another strategy must be used.
|
||||
_Extract data from spreadsheet bytes_
|
||||
|
||||
```js
|
||||
if(typeof require !== 'undefined') XLSX = require('xlsx');
|
||||
var workbook = XLSX.readFile('test.xlsx');
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
var workbook = XLSX.read(data, opts);
|
||||
```
|
||||
|
||||
The `read` method can extract data from spreadsheet bytes stored in a JS string,
|
||||
"binary string", NodeJS buffer or typed array (`Uint8Array` or `ArrayBuffer`).
|
||||
|
||||
|
||||
_Read spreadsheet bytes from a local file and extract data_
|
||||
|
||||
```js
|
||||
var workbook = XLSX.readFile(filename, opts);
|
||||
```
|
||||
|
||||
The `readFile` method attempts to read a spreadsheet file at the supplied path.
|
||||
Browsers generally do not allow reading files in this way (it is deemed a
|
||||
security risk), and attempts to read files in this way will throw an error.
|
||||
|
||||
The second `opts` argument is optional. ["Parsing Options"](#parsing-options)
|
||||
covers the supported properties and behaviors.
|
||||
|
||||
#### Examples
|
||||
|
||||
Here are a few common scenarios (click on each subtitle to see the code):
|
||||
|
||||
<details>
|
||||
<summary><b>Local file in a NodeJS server</b> (click to show)</summary>
|
||||
|
||||
`readFile` uses `fs.readFileSync` under the hood:
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx");
|
||||
|
||||
var workbook = XLSX.readFile("test.xlsx");
|
||||
```
|
||||
|
||||
For Node ESM, the `readFile` helper is not enabled. Instead, `fs.readFileSync`
|
||||
should be used to read the file data as a `Buffer` for use with `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);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Photoshop ExtendScript read a file</b> (click to show)</summary>
|
||||
<summary><b>User-submitted file in a web page ("Drag-and-Drop")</b> (click to show)</summary>
|
||||
|
||||
`readFile` wraps the `File` logic in Photoshop and other ExtendScript targets.
|
||||
The specified path should be an absolute path:
|
||||
For modern websites targeting Chrome 76+, `File#arrayBuffer` is recommended:
|
||||
|
||||
```js
|
||||
#include "xlsx.extendscript.js"
|
||||
/* Read test.xlsx from the Documents folder */
|
||||
var workbook = XLSX.readFile(Folder.myDocuments + '/' + 'test.xlsx');
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
// 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);
|
||||
```
|
||||
|
||||
The [`extendscript` demo](demos/extendscript/) includes a more complex example.
|
||||
For maximal compatibility, the `FileReader` API should be used:
|
||||
|
||||
```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/> demonstrates the FileReader technique.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Browser read TABLE element from page</b> (click to show)</summary>
|
||||
<summary><b>User-submitted file with an HTML INPUT element</b> (click to show)</summary>
|
||||
|
||||
The `table_to_book` and `table_to_sheet` utility functions take a DOM TABLE
|
||||
element and iterate through the child nodes.
|
||||
Starting with an HTML INPUT element with `type="file"`:
|
||||
|
||||
```js
|
||||
var workbook = XLSX.utils.table_to_book(document.getElementById('tableau'));
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
```html
|
||||
<input type="file" id="input_dom_element">
|
||||
```
|
||||
|
||||
Multiple tables on a web page can be converted to individual worksheets:
|
||||
For modern websites targeting Chrome 76+, `Blob#arrayBuffer` is recommended:
|
||||
|
||||
```js
|
||||
/* create new workbook */
|
||||
var workbook = XLSX.utils.book_new();
|
||||
// XLSX is a global from the standalone script
|
||||
|
||||
/* convert table 'table1' to worksheet named "Sheet1" */
|
||||
var ws1 = XLSX.utils.table_to_sheet(document.getElementById('table1'));
|
||||
XLSX.utils.book_append_sheet(workbook, ws1, "Sheet1");
|
||||
async function handleFileAsync(e) {
|
||||
const file = e.target.files[0];
|
||||
const data = await file.arrayBuffer();
|
||||
/* data is an ArrayBuffer */
|
||||
const workbook = XLSX.read(data);
|
||||
|
||||
/* convert table 'table2' to worksheet named "Sheet2" */
|
||||
var ws2 = XLSX.utils.table_to_sheet(document.getElementById('table2'));
|
||||
XLSX.utils.book_append_sheet(workbook, ws2, "Sheet2");
|
||||
|
||||
/* workbook now has 2 worksheets */
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
}
|
||||
input_dom_element.addEventListener("change", handleFileAsync, false);
|
||||
```
|
||||
|
||||
Alternatively, the HTML code can be extracted and parsed:
|
||||
For broader support (including IE10+), the `FileReader` approach is recommended:
|
||||
|
||||
```js
|
||||
var htmlstr = document.getElementById('tableau').outerHTML;
|
||||
var workbook = XLSX.read(htmlstr, {type:'string'});
|
||||
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);
|
||||
```
|
||||
|
||||
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Browser download file (ajax)</b> (click to show)</summary>
|
||||
<summary><b>Fetching a file in the web browser ("Ajax")</b> (click to show)</summary>
|
||||
|
||||
Note: for a more complete example that works in older browsers, check the demo
|
||||
at <http://oss.sheetjs.com/sheetjs/ajax.html>. The [`xhr` demo](demos/xhr/)
|
||||
includes more examples with `XMLHttpRequest` and `fetch`.
|
||||
For modern websites targeting Chrome 42+, `fetch` is recommended:
|
||||
|
||||
```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 */
|
||||
})();
|
||||
```
|
||||
|
||||
For broader support, the `XMLHttpRequest` approach is recommended:
|
||||
|
||||
```js
|
||||
var url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
|
||||
@ -90,116 +180,248 @@ req.onload = function(e) {
|
||||
var workbook = XLSX.read(req.response);
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
}
|
||||
};
|
||||
|
||||
req.send();
|
||||
```
|
||||
|
||||
The [`xhr` demo](demos/xhr/) includes a longer discussion and more examples.
|
||||
|
||||
<http://oss.sheetjs.com/sheetjs/ajax.html> shows fallback approaches for IE6+.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Browser drag-and-drop</b> (click to show)</summary>
|
||||
<summary><b>Local file in a PhotoShop or InDesign plugin</b> (click to show)</summary>
|
||||
|
||||
For modern browsers, `Blob#arrayBuffer` can read data from files:
|
||||
`readFile` wraps the `File` logic in Photoshop and other ExtendScript targets.
|
||||
The specified path should be an absolute path:
|
||||
|
||||
```js
|
||||
async function handleDropAsync(e) {
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
const f = evt.dataTransfer.files[0];
|
||||
const data = await f.arrayBuffer();
|
||||
const workbook = XLSX.read(data);
|
||||
#include "xlsx.extendscript.js"
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
}
|
||||
drop_dom_element.addEventListener('drop', handleDropAsync, false);
|
||||
/* Read test.xlsx from the Documents folder */
|
||||
var workbook = XLSX.readFile(Folder.myDocuments + "/test.xlsx");
|
||||
```
|
||||
|
||||
For maximal compatibility, the `FileReader` API should be used:
|
||||
The [`extendscript` demo](demos/extendscript/) includes a more complex example.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Local file in an Electron app</b> (click to show)</summary>
|
||||
|
||||
`readFile` can be used in the renderer process:
|
||||
|
||||
```js
|
||||
function handleDrop(e) {
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
var f = e.dataTransfer.files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var workbook = XLSX.read(e.target.result);
|
||||
/* From the renderer process */
|
||||
var XLSX = require("xlsx");
|
||||
|
||||
var workbook = XLSX.readFile(path);
|
||||
```
|
||||
|
||||
Electron APIs have changed over time. The [`electron` demo](demos/electron/)
|
||||
shows a complete example and details the required version-specific settings.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Local file in a mobile app with React Native</b> (click to show)</summary>
|
||||
|
||||
The [`react` demo](demos/react) includes a sample React Native app.
|
||||
|
||||
Since React Native does not provide a way to read files from the filesystem, a
|
||||
third-party library must be used. The following libraries have been tested:
|
||||
|
||||
- [`react-native-file-access`](https://npm.im/react-native-file-access)
|
||||
|
||||
The `base64` encoding returns strings compatible with the `base64` type:
|
||||
|
||||
```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)
|
||||
|
||||
The `ascii` encoding returns binary strings compatible with the `binary` type:
|
||||
|
||||
```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 Server File Uploads</b> (click to show)</summary>
|
||||
|
||||
`read` can accept a NodeJS buffer. `readFile` can read files generated by a
|
||||
HTTP POST request body parser like [`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 */
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
}
|
||||
drop_dom_element.addEventListener('drop', handleDrop, false);
|
||||
});
|
||||
}).listen(process.env.PORT || 7262);
|
||||
```
|
||||
|
||||
The [`server` demo](demos/server) has more advanced examples.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Browser file upload form element</b> (click to show)</summary>
|
||||
<summary><b>Download files in a NodeJS process</b> (click to show)</summary>
|
||||
|
||||
Data from file input elements can be processed using the same APIs as in the
|
||||
drag-and-drop example.
|
||||
|
||||
Using `Blob#arrayBuffer`:
|
||||
Node 17.5 and 18.0 have native support for fetch:
|
||||
|
||||
```js
|
||||
async function handleFileAsync(e) {
|
||||
const file = e.target.files[0];
|
||||
const data = await file.arrayBuffer();
|
||||
const workbook = XLSX.read(data);
|
||||
const XLSX = require("xlsx");
|
||||
|
||||
const data = await (await fetch(url)).arrayBuffer();
|
||||
/* data is an ArrayBuffer */
|
||||
const workbook = XLSX.read(data);
|
||||
```
|
||||
|
||||
For broader compatibility, third-party modules are recommended.
|
||||
|
||||
[`request`](https://npm.im/request) requires a `null` encoding to yield 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 */
|
||||
}
|
||||
input_dom_element.addEventListener('change', handleFileAsync, false);
|
||||
});
|
||||
```
|
||||
|
||||
Using `FileReader`:
|
||||
[`axios`](https://npm.im/axios) works the same way in browser and in NodeJS:
|
||||
|
||||
```js
|
||||
function handleFile(e) {
|
||||
var files = e.target.files, f = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var workbook = XLSX.read(e.target.result);
|
||||
const XLSX = require("xlsx");
|
||||
const axios = require("axios");
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
}
|
||||
input_dom_element.addEventListener('change', handleFile, false);
|
||||
(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 */
|
||||
})();
|
||||
```
|
||||
|
||||
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
|
||||
|
||||
</details>
|
||||
|
||||
More specialized cases, including mobile app file processing, are covered in the
|
||||
[included demos](demos/)
|
||||
|
||||
### Parsing Examples
|
||||
|
||||
- <https://oss.sheetjs.com/sheetjs/> HTML5 File API / Base64 Text / Web Workers
|
||||
|
||||
Note that older versions of IE do not support HTML5 File API, so the Base64 mode
|
||||
is used for testing.
|
||||
|
||||
<details>
|
||||
<summary><b>Get Base64 encoding on OSX / Windows</b> (click to show)</summary>
|
||||
<summary><b>Download files in an Electron app</b> (click to show)</summary>
|
||||
|
||||
On OSX you can get the Base64 encoding with:
|
||||
The `net` module in the main process can make HTTP/HTTPS requests to external
|
||||
resources. Responses should be manually concatenated using `Buffer.concat`:
|
||||
|
||||
```bash
|
||||
$ <target_file base64 | pbcopy
|
||||
```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();
|
||||
```
|
||||
|
||||
On Windows XP and up you can get the Base64 encoding using `certutil`:
|
||||
|
||||
```cmd
|
||||
> certutil -encode target_file target_file.b64
|
||||
```
|
||||
|
||||
(note: You have to open the file and remove the header and footer lines)
|
||||
|
||||
</details>
|
||||
|
||||
- <http://oss.sheetjs.com/sheetjs/ajax.html> XMLHttpRequest
|
||||
<details>
|
||||
<summary><b>Readable Streams in NodeJS</b> (click to show)</summary>
|
||||
|
||||
When dealing with Readable Streams, the easiest approach is to buffer the stream
|
||||
and process the whole thing at the end:
|
||||
|
||||
```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 in the browser</b> (click to show)</summary>
|
||||
|
||||
When dealing with `ReadableStream`, the easiest approach is to buffer the stream
|
||||
and process the whole thing at the end:
|
||||
|
||||
```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>
|
||||
|
||||
More detailed examples are covered in the [included demos](demos/)
|
||||
|
||||
|
||||
|
@ -1,67 +0,0 @@
|
||||
### Streaming Read
|
||||
|
||||
<details>
|
||||
<summary><b>Why is there no Streaming Read API?</b> (click to show)</summary>
|
||||
|
||||
The most common and interesting formats (XLS, XLSX/M, XLSB, ODS) are ultimately
|
||||
ZIP or CFB containers of files. Neither format puts the directory structure at
|
||||
the beginning of the file: ZIP files place the Central Directory records at the
|
||||
end of the logical file, while CFB files can place the storage info anywhere in
|
||||
the file! As a result, to properly handle these formats, a streaming function
|
||||
would have to buffer the entire file before commencing. That belies the
|
||||
expectations of streaming, so we do not provide any streaming read API.
|
||||
|
||||
</details>
|
||||
|
||||
When dealing with Readable Streams, the easiest approach is to buffer the stream
|
||||
and process the whole thing at the end. This can be done with a temporary file
|
||||
or by explicitly concatenating the stream:
|
||||
|
||||
<details>
|
||||
<summary><b>Explicitly concatenating streams</b> (click to show)</summary>
|
||||
|
||||
```js
|
||||
var fs = require('fs');
|
||||
var XLSX = require('xlsx');
|
||||
function process_RS(stream/*:ReadStream*/, cb/*:(wb:Workbook)=>void*/)/*:void*/{
|
||||
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);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
More robust solutions are available using modules like `concat-stream`.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Writing to filesystem first</b> (click to show)</summary>
|
||||
|
||||
This example uses [`tempfile`](https://npm.im/tempfile) to generate file names:
|
||||
|
||||
```js
|
||||
var fs = require('fs'), tempfile = require('tempfile');
|
||||
var XLSX = require('xlsx');
|
||||
function process_RS(stream/*:ReadStream*/, cb/*:(wb:Workbook)=>void*/)/*:void*/{
|
||||
var fname = tempfile('.sheetjs');
|
||||
console.log(fname);
|
||||
var ostream = fs.createWriteStream(fname);
|
||||
stream.pipe(ostream);
|
||||
ostream.on('finish', function() {
|
||||
var workbook = XLSX.readFile(fname);
|
||||
fs.unlinkSync(fname);
|
||||
|
||||
/* DO SOMETHING WITH workbook IN THE CALLBACK */
|
||||
cb(workbook);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
182
docbits/22_ingress.md
Normal file
182
docbits/22_ingress.md
Normal file
@ -0,0 +1,182 @@
|
||||
### Processing JSON and JS Data
|
||||
|
||||
JSON and JS data tend to represent single worksheets. This section will use a
|
||||
few utility functions to generate workbooks:
|
||||
|
||||
_Create a new Worksheet_
|
||||
|
||||
```js
|
||||
var workbook = XLSX.utils.book_new();
|
||||
```
|
||||
|
||||
The `book_new` utility function creates an empty workbook with no worksheets.
|
||||
|
||||
|
||||
_Append a Worksheet to a Workbook_
|
||||
|
||||
```js
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
|
||||
```
|
||||
|
||||
The `book_append_sheet` utility function appends a worksheet to the workbook.
|
||||
The third argument specifies the desired worksheet name. Multiple worksheets can
|
||||
be added to a workbook by calling the function multiple times.
|
||||
|
||||
|
||||
#### API
|
||||
|
||||
_Create a worksheet from an array of arrays of JS values_
|
||||
|
||||
```js
|
||||
var worksheet = XLSX.utils.aoa_to_sheet(aoa, opts);
|
||||
```
|
||||
|
||||
The `aoa_to_sheet` utility function walks an "array of arrays" in row-major
|
||||
order, generating a worksheet object. The following snippet generates a sheet
|
||||
with cell `A1` set to the string `A1`, cell `B1` set to `B2`, etc:
|
||||
|
||||
```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) describes the function and the
|
||||
optional `opts` argument in more detail.
|
||||
|
||||
|
||||
_Create a worksheet from an array of JS objects_
|
||||
|
||||
```js
|
||||
var worksheet = XLSX.utils.json_to_sheet(jsa, opts);
|
||||
```
|
||||
|
||||
The `json_to_sheet` utility function walks an array of JS objects in order,
|
||||
generating a worksheet object. By default, it will generate a header row and
|
||||
one row per object in the array. The optional `opts` argument has settings to
|
||||
control the column order and header output.
|
||||
|
||||
["Array of Objects Input"](#array-of-arrays-input) describes the function and
|
||||
the optional `opts` argument in more detail.
|
||||
|
||||
#### Examples
|
||||
|
||||
["Zen of SheetJS"](#the-zen-of-sheetjs) contains a detailed example "Get Data
|
||||
from a JSON Endpoint and Generate a Workbook"
|
||||
|
||||
The [`database` demo](/demos/database/) includes examples of working with
|
||||
databases and query results.
|
||||
|
||||
### Processing HTML Tables
|
||||
|
||||
#### API
|
||||
|
||||
_Create a worksheet by scraping an HTML TABLE in the page_
|
||||
|
||||
```js
|
||||
var worksheet = XLSX.utils.table_to_sheet(dom_element, opts);
|
||||
```
|
||||
|
||||
The `table_to_sheet` utility function takes a DOM TABLE element and iterates
|
||||
through the rows to generate a worksheet. The `opts` argument is optional.
|
||||
["HTML Table Input"](#html-table-input) describes the function in more detail.
|
||||
|
||||
|
||||
|
||||
_Create a workbook by scraping an HTML TABLE in the page_
|
||||
|
||||
```js
|
||||
var workbook = XLSX.utils.table_to_book(dom_element, opts);
|
||||
```
|
||||
|
||||
The `table_to_book` utility function follows the same logic as `table_to_sheet`.
|
||||
After generating a worksheet, it creates a blank workbook and appends the
|
||||
spreadsheet.
|
||||
|
||||
The options argument supports the same options as `table_to_sheet`, with the
|
||||
addition of a `sheet` property to control the worksheet name. If the property
|
||||
is missing or no options are specified, the default name `Sheet1` is used.
|
||||
|
||||
#### Examples
|
||||
|
||||
Here are a few common scenarios (click on each subtitle to see the code):
|
||||
|
||||
<details>
|
||||
<summary><b>HTML TABLE element in a webpage</b> (click to show)</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>
|
||||
```
|
||||
|
||||
Multiple tables on a web page can be converted to individual worksheets:
|
||||
|
||||
```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 */
|
||||
```
|
||||
|
||||
Alternatively, the HTML code can be extracted and parsed:
|
||||
|
||||
```js
|
||||
var htmlstr = document.getElementById("tableau").outerHTML;
|
||||
var workbook = XLSX.read(htmlstr, {type:"string"});
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Chrome/Chromium Extension</b> (click to show)</summary>
|
||||
|
||||
The [`chrome` demo](demos/chrome/) shows a complete example and details the
|
||||
required permissions and other settings.
|
||||
|
||||
In an extension, it is recommended to generate the workbook in a content script
|
||||
and pass the object back to the extension:
|
||||
|
||||
```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>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,11 +4,18 @@
|
||||
- [Getting Started](README.md#getting-started)
|
||||
* [Installation](README.md#installation)
|
||||
* [Usage](README.md#usage)
|
||||
+ [The Zen of SheetJS](README.md#the-zen-of-sheetjs)
|
||||
* [The Zen of SheetJS](README.md#the-zen-of-sheetjs)
|
||||
* [JS Ecosystem Demos](README.md#js-ecosystem-demos)
|
||||
- [Parsing Workbooks](README.md#parsing-workbooks)
|
||||
* [Parsing Examples](README.md#parsing-examples)
|
||||
* [Streaming Read](README.md#streaming-read)
|
||||
- [Acquiring and Extracting Data](README.md#acquiring-and-extracting-data)
|
||||
* [Parsing Workbooks](README.md#parsing-workbooks)
|
||||
+ [API](README.md#api)
|
||||
+ [Examples](README.md#examples)
|
||||
* [Processing JSON and JS Data](README.md#processing-json-and-js-data)
|
||||
+ [API](README.md#api-1)
|
||||
+ [Examples](README.md#examples-1)
|
||||
* [Processing HTML Tables](README.md#processing-html-tables)
|
||||
+ [API](README.md#api-2)
|
||||
+ [Examples](README.md#examples-2)
|
||||
- [Working with the Workbook](README.md#working-with-the-workbook)
|
||||
* [Parsing and Writing Examples](README.md#parsing-and-writing-examples)
|
||||
- [Writing Workbooks](README.md#writing-workbooks)
|
||||
|
21
modules/59_vba.js
Normal file
21
modules/59_vba.js
Normal file
@ -0,0 +1,21 @@
|
||||
var CT_VBA = "application/vnd.ms-office.vbaProject";
|
||||
function make_vba_xls(cfb) {
|
||||
var newcfb = CFB.utils.cfb_new({ root: "R" });
|
||||
cfb.FullPaths.forEach(function(p, i) {
|
||||
if (p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/))
|
||||
return;
|
||||
var newpath = p.replace(/^[^\/]*/, "R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
|
||||
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
|
||||
});
|
||||
return CFB.write(newcfb);
|
||||
}
|
||||
function fill_vba_xls(cfb, vba) {
|
||||
vba.FullPaths.forEach(function(p, i) {
|
||||
if (i == 0)
|
||||
return;
|
||||
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
|
||||
if (newpath.slice(-1) !== "/")
|
||||
CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
|
||||
});
|
||||
}
|
||||
var VBAFMTS = ["xlsb", "xlsm", "xlam", "biff8", "xla"];
|
24
modules/59_vba.ts
Normal file
24
modules/59_vba.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import * as CFBModule from 'cfb';
|
||||
type CFBType = typeof CFBModule;
|
||||
declare var CFB: CFBType;
|
||||
|
||||
var CT_VBA = "application/vnd.ms-office.vbaProject";
|
||||
function make_vba_xls(cfb: CFBModule.CFB$Container) {
|
||||
var newcfb = CFB.utils.cfb_new({root:"R"});
|
||||
cfb.FullPaths.forEach(function(p, i) {
|
||||
if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
|
||||
var newpath = p.replace(/^[^\/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
|
||||
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
|
||||
});
|
||||
return CFB.write(newcfb);
|
||||
}
|
||||
|
||||
function fill_vba_xls(cfb: CFBModule.CFB$Container, vba: CFBModule.CFB$Container): void {
|
||||
vba.FullPaths.forEach(function(p, i) {
|
||||
if(i == 0) return;
|
||||
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
|
||||
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
|
||||
});
|
||||
}
|
||||
|
||||
var VBAFMTS: string[] = [ "xlsb", "xlsm", "xlam", "biff8", "xla" ];
|
1133
modules/64_ftab.js
Normal file
1133
modules/64_ftab.js
Normal file
File diff suppressed because it is too large
Load Diff
1138
modules/64_ftab.ts
Normal file
1138
modules/64_ftab.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,10 +2,12 @@ LIBFILES=$(wildcard src/*.ts)
|
||||
TSFILES=$(wildcard *.ts)
|
||||
ENTRIES=$(subst .ts,.js,$(TSFILES))
|
||||
|
||||
BAREJS=04_base64.js 59_vba.js 64_ftab.js
|
||||
|
||||
.PHONY: all
|
||||
all: $(ENTRIES)
|
||||
|
||||
04_base64.js: 04_base64.ts $(LIBFILES)
|
||||
$(BAREJS): %.js: %.ts $(LIBFILES)
|
||||
npx esbuild $< --outfile=$@ --platform=browser --target=es5
|
||||
|
||||
83_numbers.js: 83_numbers.ts $(LIBFILES)
|
||||
|
Loading…
Reference in New Issue
Block a user