This commit is contained in:
SheetJS 2022-10-19 06:05:59 -04:00
parent 62cfee92ef
commit e50dde6b20
17 changed files with 355 additions and 44 deletions

@ -35,8 +35,6 @@ The `@deno-types` comment instructs Deno to use the type definitions.
Older releases are technically available on [deno.land/x](https://deno.land/x/)
but the Deno registry is out of date.
[This is a known registry bug](https://github.com/denoland/dotland/issues/2072)
<https://cdn.sheetjs.com/> is the authoritative source for SheetJS scripts.
:::

@ -327,7 +327,7 @@ This example tries to separate the library-specific functions.
0) **Follow the official React Native CLI Guide!**
Development Environment Guide: <http://reactnative.dev/docs/environment-setup>
Development Environment Guide: <https://reactnative.dev/docs/environment-setup>
Follow the instructions for iOS and for Android. They will cover installation
and system configuration. By the end, you should be able to run the sample app
@ -676,7 +676,7 @@ on an iPhone SE 3rd generation.
NativeScript will not safely transmit binary or UTF-8 strings. XLSB, NUMBERS,
XLSX, XLS, ODS, SYLK, and DBF exports are known to be mangled.
[This is a known NativeScript bug](https://github.com/NativeScript/NativeScript/issues/9586)
This is a known NativeScript bug.
This demo will focus on ASCII CSV files. Once the bug is resolved, XLSX and
other formats will be supported.

@ -142,7 +142,7 @@ This demo was tested against Electron 19.0.5 on an Intel Mac (`darwin-x64`).
<details><summary><b>Complete Example</b> (click to show)</summary>
This demo includes a drag-and-drop box as well as a file input box, mirroring
the [SheetJS Data Preview Live Demo](http://oss.sheetjs.com/sheetjs/)
the [SheetJS Data Preview Live Demo](https://oss.sheetjs.com/sheetjs/)
The core data in this demo is an editable HTML table. The readers build up the
table using `sheet_to_html` (with `editable:true` option) and the writers scrape
@ -336,7 +336,7 @@ backwards compatibility multiple times. A summary of changes is noted below.
Electron 6.x changed the `dialog` API. Methods like `showSaveDialog` originally
returned an array of strings, but now returns a `Promise`. This change was not
documented. [Electron issue](https://github.com/electron/electron/issues/24438)
documented.
Electron 9.0.0 and later require the preference `nodeIntegration: true` in order
to `require('xlsx')` in the renderer process.
@ -1381,7 +1381,7 @@ At the time of writing, the latest supported React Native version was `v0.64.3`
NodeJS `v16` is required. There are OS-specific tools for downgrading:
- [`nvm-windows`](https://github.com/coreybutler/nvm-windows/releases) Windows
- [`nvm`](https://github.com/nvm-sh/nvm/) Linux, MacOS, WSL, etc.
- [`n`](https://github.com/tj/n/) Linux, MacOS, WSL, etc.
:::

@ -275,9 +275,9 @@ export as XLSX.
<Tabs>
<TabItem value="nodejs" label="NodeJS">
[The `better-sqlite3` module](https://www.npmjs.com/package/better-sqlite3)
provides a very simple API for working with SQLite databases. `Statement#all`
runs a prepared statement and returns an array of JS objects.
The **`better-sqlite3`** module provides a very simple API for working with
SQLite databases. `Statement#all` runs a prepared statement and returns an array
of JS objects.
1) Install the dependencies:
@ -440,7 +440,7 @@ WebSQL was a popular SQL-based in-browser database available on Chrome. In
practice, it is powered by SQLite, and most simple SQLite-compatible queries
work as-is in WebSQL.
The public demo <http://sheetjs.com/sql> generates a database from workbook.
The public demo <https://sheetjs.com/sql> generates a database from workbook.
Importing data from spreadsheets is straightforward using the `generate_sql`
helper function from ["Building Schemas"](#building-schemas-from-worksheets):
@ -783,7 +783,7 @@ MongoDB is a popular document-oriented database engine.
It is straightforward to treat collections as worksheets. Each object maps to
a row in the table.
The official NodeJS connector is [`mongodb` on NPM](https://npm.im/mongodb).
The official NodeJS connector is **`mongodb`**.
Worksheets can be generated from collections by using `Collection#find`. A
`projection` can suppress the object ID field:

@ -0,0 +1,219 @@
---
title: Web Workers
---
Parsing and writing large spreadsheets takes time. During the process, if the
SheetJS library is running in the web browser, the website may freeze.
Workers provide a way to off-load the hard work so that the website does not
freeze during processing.
:::note Browser Compatibility
IE10+ and modern browsers support basic Web Workers. Some APIs like `fetch` were
added later. Feature testing is highly recommended.
:::
## Installation
In all cases, `importScripts` can load the [Standalone scripts](../getting-started/installation/standalone)
```js
importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
```
For production use, it is highly encouraged to download and host the script.
## Downloading a Remote File
:::note
`fetch` was enabled in Web Workers in Chrome 42 and Safari 10.3
:::
Typically the Web Worker performs the `fetch` operation, processes the workbook,
and sends a final result to the main browser context for processing.
In the following example, the script:
- downloads <https://sheetjs.com/pres.numbers> in a Web Worker
- loads the SheetJS library and parses the file in the Worker
- generates an HTML string of the first table in the Worker
- sends the string to the main browser context
- adds the HTML to the page in the main browser context
```jsx live
function SheetJSFetchDLWorker() {
const [html, setHTML] = React.useState("");
return ( <>
<button onClick={() => {
/* this mantra embeds the worker source in the function */
const worker = new Worker(URL.createObjectURL(new Blob([`\
/* load standalone script from CDN */
importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
/* this callback will run once the main context sends a message */
self.addEventListener('message', async(e) => {
try {
/* Fetch file */
const res = await fetch("https://sheetjs.com/pres.numbers");
const ab = await res.arrayBuffer();
/* Parse file */
const wb = XLSX.read(ab);
const ws = wb.Sheets[wb.SheetNames[0]];
/* Generate HTML */
const html = XLSX.utils.sheet_to_html(ws);
/* Reply with result */
postMessage({html: html});
} catch(e) {
/* Pass the error message back */
postMessage({html: String(e.message || e).bold() });
}
}, false);
`])));
/* when the worker sends back the HTML, add it to the DOM */
worker.onmessage = function(e) { setHTML(e.data.html); };
/* post a message to the worker */
worker.postMessage({});
}}><b>Click to Start</b></button>
<div dangerouslySetInnerHTML={{__html: html}}/>
</> );
}
```
## Creating a Local File
:::caution `XLSX.writeFile`
`XLSX.writeFile` will not work in Web Workers! Raw file data can be passed from
the Web Worker to the main browser context for downloading.
:::
In the following example, the script:
- generates a workbook object in the Web Worker
- generates a XLSB file using `XLSX.write` in the Web Worker
- sends the file (`Uint8Array`) to the main browser context
- performs a download action in the main browser context
```jsx live
function SheetJSWriteFileWorker() {
const [html, setHTML] = React.useState("");
return ( <>
<button onClick={() => { setHTML("");
/* this mantra embeds the worker source in the function */
const worker = new Worker(URL.createObjectURL(new Blob([`\
/* load standalone script from CDN */
importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
/* this callback will run once the main context sends a message */
self.addEventListener('message', async(e) => {
try {
/* Create a new Workbook (in this case, from a CSV string) */
const csv = \`\
SheetJS,in,Web,Workers
வணக்கம்,สวัสดี,你好,가지마
1,2,3,4\`;
const wb = XLSX.read(csv, { type: "string" });
/* Write XLSB data */
const u8 = XLSX.write(wb, { bookType: "xlsb", type: "buffer" });
/* Reply with result */
postMessage({data: u8});
} catch(e) {
/* Pass the error message back */
postMessage({error: String(e.message || e).bold() });
}
}, false);
`])));
/* when the worker sends back the data, create a download */
worker.onmessage = function(e) {
if(e.data.error) return setHTML(e.data.error);
/* this mantra is the standard HTML5 download attribute technique */
const a = document.createElement("a");
a.download = "SheetJSWriteFileWorker.xlsb";
a.href = URL.createObjectURL(new Blob([e.data.data]));
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
/* post a message to the worker */
worker.postMessage({});
}}><b>Click to Start</b></button>
<div dangerouslySetInnerHTML={{__html: html}}/>
</> );
}
```
## User-Submitted File
:::note
Typically `FileReader` is used in the main browser context. In Web Workers, the
synchronous version `FileReaderSync` is more efficient.
:::
In the following example, the script:
- waits for the user to drag-drop a file into a DIV
- sends the `File` object to the Web Worker
- loads the SheetJS library and parses the file in the Worker
- generates an HTML string of the first table in the Worker
- sends the string to the main browser context
- adds the HTML to the page in the main browser context
```jsx live
function SheetJSDragDropWorker() {
const [html, setHTML] = React.useState("");
/* suppress default behavior for dragover and drop */
function suppress(e) { e.stopPropagation(); e.preventDefault(); }
return ( <>
<div onDragOver={suppress} onDrop={(e) => {
suppress(e);
/* this mantra embeds the worker source in the function */
const worker = new Worker(URL.createObjectURL(new Blob([`\
/* load standalone script from CDN */
importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
/* this callback will run once the main context sends a message */
self.addEventListener('message', async(e) => {
try {
/* Read file data */
const ab = new FileReaderSync().readAsArrayBuffer(e.data.file);
/* Parse file */
const wb = XLSX.read(ab);
const ws = wb.Sheets[wb.SheetNames[0]];
/* Generate HTML */
const html = XLSX.utils.sheet_to_html(ws);
/* Reply with result */
postMessage({html: html});
} catch(e) {
/* Pass the error message back */
postMessage({html: String(e.message || e).bold() });
}
}, false);
`])));
/* when the worker sends back the HTML, add it to the DOM */
worker.onmessage = function(e) { setHTML(e.data.html); };
/* post a message with the first File to the worker */
worker.postMessage({ file: e.dataTransfer.files[0] });
}}>Drag a file to this DIV to process!</div>
<div dangerouslySetInnerHTML={{__html: html}}/>
</> );
}
```

@ -230,7 +230,7 @@ Google and the Angular team.
The Angular tooling provides no easy way to switch between versions!
[This is a known Angular problem](https://github.com/angular/angular-cli/issues/9047)
This is a known Angular problem.
To work around this, [`SheetJSAngular.zip`](pathname:///angular/SheetJSAngular.zip)
is a skeleton project designed to play nice with each Angular version.

@ -349,9 +349,8 @@ Parcel should play nice with SheetJS out of the box.
:::warning Parcel Bug
Errors of the form `Could not statically evaluate fs call` stem from a
[parcel bug](https://github.com/parcel-bundler/parcel/pull/523). Upgrade to
Parcel version 1.5.0 or later.
Errors of the form `Could not statically evaluate fs call` stem from a Parcel
bug. Upgrade to Parcel version 1.5.0 or later.
:::

@ -68,24 +68,6 @@ const u8 = XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });
const blob = new Blob([u8], { type: "application/vnd.ms-excel" });
```
## Web Workers
:::warning
**None of the browser methods work from Web Worker contexts!**
Data operations with the Web APIs must happen in the browser main thread.
:::
Web Workers and main thread can transfer `ArrayBuffer` or `Uint8Array` objects.
When generating a file, the worker will call `XLSX.write` with type `buffer`
and transfer the result to the main thread to initiate a download.
When parsing a file, the main thread will use the web API to read a `File` or
`Blob`, extract the underlying `ArrayBuffer` and transfer to the Web Worker.
## HTML5 Download Attribute
_Writing Files_

@ -176,7 +176,7 @@ import * as XLSX from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
```
The official registry endpoint <https://deno.land/x/sheetjs> is out of date.
[This is a known registry bug](https://github.com/denoland/dotland/issues/2072)
This is a known registry bug.
:::

@ -12,11 +12,12 @@ run in the web browser, demos will include interactive examples.
- [`XMLHttpRequest and fetch`](./network)
- [`Clipboard Data`](./clipboard)
- [`Web Workers`](./worker)
- [`Typed Arrays for Machine Learning`](./ml)
- [`Local File Access`](./localfile)
- [`LocalStorage and SessionStorage`](./database#localstorage-and-sessionstorage)
- [`Web SQL Database`](./database#websql)
- [`IndexedDB`](./database#indexeddb)
- [`Local File Access`](./localfile)
### Frameworks
@ -36,6 +37,7 @@ run in the web browser, demos will include interactive examples.
- [`vue3-table-lite`](./grid#vue3-table-lite)
- [`angular-ui-grid`](./grid#angular-ui-grid)
- [`material ui`](./grid#material-ui-table)
### Platforms and Integrations
- [`Command-Line Tools`](./cli)

@ -301,7 +301,7 @@ The [`oldie` demo](../demos/legacy#internet-explorer) shows an IE-compatible fal
<TabItem value="nodejs" label="NodeJS">
`read` can accept a NodeJS buffer. `readFile` can read files generated by a
HTTP POST request body parser like [`formidable`](https://npm.im/formidable):
HTTP POST request body parser like **`formidable`**:
```js
const XLSX = require("xlsx");
@ -444,7 +444,7 @@ 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:
**`request`** requires a `null` encoding to yield Buffers:
```js
var XLSX = require("xlsx");
@ -458,7 +458,7 @@ request({url: url, encoding: null}, function(err, resp, body) {
});
```
[`axios`](https://axios-http.com/) works the same way in browser and in NodeJS:
**`axios`** works the same way in browser and in NodeJS:
```js
const XLSX = require("xlsx");
@ -861,7 +861,7 @@ chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
<summary><b>NodeJS HTML Tables without a browser</b> (click to show)</summary>
NodeJS does not include a DOM implementation and Puppeteer requires a hefty
Chromium build. [`jsdom`](https://npm.im/jsdom) is a lightweight alternative:
Chromium build. **`jsdom`** is a lightweight alternative:
```js
const XLSX = require("xlsx");

@ -10,9 +10,9 @@ title: API Reference
`XLSX.version` is the version of the library (added by the build script).
`XLSX.SSF` is an embedded version of the [format library](https://github.com/SheetJS/sheetjs/tree/master/packages/ssf).
`XLSX.SSF` is an embedded version of the [format library](https://git.sheetjs.com/sheetjs/sheetjs/src/branch/master/packages/ssf).
`XLSX.CFB` is an embedded version of the [container library](https://github.com/sheetjs/js-cfb).
`XLSX.CFB` is an embedded version of the [container library](https://git.sheetjs.com/sheetjs/js-cfb).
## Parsing functions

@ -6,7 +6,14 @@ hide_table_of_contents: true
The official source code repository is <https://git.sheetjs.com/sheetjs/sheetjs>
Mirrors:
:::note Mirrors
Older snapshots of the source code repository are available at various hosts:
- [GitHub](https://github.com/sheetjs/sheetjs)
- [GitLab](https://gitlab.com/sheetjs/sheetjs)
- [BitBucket](https://bitbucket.org/sheetjs/sheetjs)
<https://git.sheetjs.com/sheetjs/sheetjs> is the authoritative repository.
:::

@ -5,7 +5,7 @@ sidebar_position: 5
# Contributing
Due to the precarious nature of the Open Specifications Promise, it is very
important to ensure code is cleanroom. [Contribution Notes](https://raw.githubusercontent.com/SheetJS/sheetjs/master/CONTRIBUTING.md)
important to ensure code is cleanroom. [Contribution Notes](https://git.sheetjs.com/sheetjs/sheetjs/src/branch/master/CONTRIBUTING.md)
<details>
<summary><b>File organization</b> (click to show)</summary>

@ -0,0 +1,23 @@
<body>
<style>TABLE { border-collapse: collapse; } TD { border: 1px solid; }</style>
<div id="tavolo"></div>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/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("https://sheetjs.com/pres.numbers")).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>

@ -0,0 +1,38 @@
<!DOCTYPE html>
<!-- sheetjs (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html lang="en" style="height: 100%">
<body>
<style>TABLE { border-collapse: collapse; } TD { border: 1px solid; }</style>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function Titel(props) { return ( <h3>{props.name}</h3> ); }
function Tabeller(props) {
/* the workbook object is the state */
const [workbook, setWorkbook] = React.useState(XLSX.utils.book_new());
React.useEffect(() => { (async() => {
/* fetch and parse workbook -- see the fetch example for details */
const wb = XLSX.read(await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer());
setWorkbook(wb);
})(); });
return workbook.SheetNames.map((name, idx) => ( <div key={idx}>
<Titel name={name} />
<div dangerouslySetInnerHTML={{
/* this __html mantra is needed to set the inner HTML */
__html: XLSX.utils.sheet_to_html(workbook.Sheets[name])
}} />
</div>));
}
ReactDOM.render( <Tabeller/> , root );
</script>
</body>
</html>

@ -0,0 +1,43 @@
<!DOCTYPE html>
<!-- sheetjs (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html lang="en" style="height: 100%">
<body>
<style>TABLE { border-collapse: collapse; } TD { border: 1px solid; }</style>
<script type="importmap">{
"imports": {
"vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js",
"xlsx": "https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs"
}
}</script>
<div id="root"></div>
<script type="module">
import { createApp, reactive } from 'vue';
import { read, utils } from 'xlsx';
const S5SComponent = {
mounted() { (async() => {
/* fetch and parse workbook -- see the fetch example for details */
const workbook = read(await (await fetch("https://sheetjs.com/pres.numbers")).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>`
};
createApp(S5SComponent).mount('#root');
</script>
</body>
</html>