localstorage

This commit is contained in:
SheetJS 2023-02-26 06:38:03 -05:00
parent d54a1e4ee0
commit dbe0554b9b
4 changed files with 155 additions and 27 deletions

@ -4,8 +4,10 @@ pagination_prev: demos/desktop/index
pagination_next: demos/grid
---
At the time of writing (2023 February 15), Airtable recommends Personal Access
Tokens for interacting with the official API.
Airtable recommends Personal Access Tokens for interacting with their API. When
fetching data from the API, the result will include an array of row objects that
can be converted to a worksheet with `XLSX.utils.json_to_sheet`. The API methods
to write data will accept row objects generated by `XLSX.utils.sheet_to_json`.
## NodeJS Integration
@ -76,6 +78,13 @@ async function airtable_load_worksheet(table, worksheet) {
## Complete Example
:::note
This demo was last tested on 2023 February 15. At the time, it was possible to
create a free account with API access.
:::
0) Create a free Airtable account.
### Personal Access Token

@ -9,23 +9,80 @@ sidebar_custom_props:
The Storage API, encompassing `localStorage` and `sessionStorage`, describes
simple key-value stores that only support string values and keys.
Arrays of objects can be stored using `JSON.stringify` using row index as key:
This demo covers two common use patterns:
- "Row Objects" shows a simple convention for loading and storing row objects
- "Simple Strings" discusses how to persist and recover a raw Storage
## Row Objects
Consider the following array of objects of data:
```js
const aoo = XLSX.utils.sheet_to_json(ws);
for(var i = 0; i < aoo.length; ++i) localStorage.setItem(i, JSON.stringify(aoo[i]));
[
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 },
{ Name: "Joseph Biden", Index: 46 }
]
```
Recovering the array of objects is possible by using `JSON.parse`:
Storage API expects values to be strings. The simplest approach is to stringify
row objects using `JSON.stringify` and store using the row index as a key:
| Key | Value |
|:---:|:-------------------------------------|
| 0 | `{"Name":"Barack Obama","Index":44}` |
| 1 | `{"Name":"Donald Trump","Index":45}` |
| 2 | `{"Name":"Joseph Biden","Index":46}` |
#### Importing Data
Starting from a worksheet, `XLSX.utils.sheet_to_json` generates an array of row
objects. `localStorage.setItem` will store data in Local Storage:
```js
const aoo = [];
for(var i = 0; i < localStorage.length; ++i) aoo.push(JSON.parse(localStorage.getItem(i)));
const ws = XLSX.utils.json_to_sheet(aoo);
function sheet_to_localStorage(worksheet) {
const aoo = XLSX.utils.sheet_to_json(worksheet);
for(let i = 0; i < aoo.length; ++i) {
localStorage.setItem(i, JSON.stringify(aoo[i]));
}
}
```
This example will fetch <https://sheetjs.com/data/cd.xls>, fill `localStorage` with
rows, then generate a worksheet from the rows and write to a new file.
#### Exporting Data
`localStorage.length` returns the total number of entries. A simple `for` loop
can cover the keys (integers from `0` to `localStorage.length - 1` inclusive)
`localStorage.getItem` will load the stringified data from the Local Storage. A
new array of objects can be constructed by using `JSON.parse` and pushing to an
array. `XLSX.utils.json_to_sheet` can create a new worksheet from that array:
```js
function localStorage_to_sheet() {
const aoo = [];
for(let i = 0; i < localStorage.length; ++i) {
aoo.push(JSON.parse(localStorage.getItem(i)));
}
return XLSX.utils.json_to_sheet(aoo);
}
```
### Live Demo
:::note
This demo was last tested on 2023 February 26.
:::
This example will fetch <https://sheetjs.com/pres.numbers>, fill `localStorage`
with rows, then generate a worksheet from the rows and write to a new file.
After saving the exported file, the Local Storage can be inspected in the
"Local Storage" section of the "Application" Tab of Developer Tools:
![Local Storage view in Developer Tools](pathname:///storageapi/lstorage.png)
:::caution
@ -36,14 +93,13 @@ is strongly recommended to convert that array to a worksheet directly.
```jsx live
function SheetJStorage() {
const [url, setUrl] = React.useState("https://sheetjs.com/data/cd.xls");
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
const set_url = React.useCallback((evt) => setUrl(evt.target.value));
const [out, setOut] = React.useState("");
const xport = React.useCallback(async() => {
// get first worksheet data as array of objects
const ab = await (await fetch(url)).arrayBuffer();
const wb = XLSX.read(ab), wsname = wb.SheetNames[0];
const aoo = XLSX.utils.sheet_to_json(wb.Sheets[wsname]);
const wb = XLSX.read(await (await fetch(url)).arrayBuffer());
const aoo = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
// reset and populate localStorage
localStorage.clear();
@ -65,9 +121,59 @@ function SheetJStorage() {
XLSX.writeFile(new_wb, "SheetJStorage.xlsx");
});
return ( <> {out && (<><a href={url}>{url}</a><pre>{out}</pre></>)}
return ( <>
{out && (<><a href={url}>{url}</a><pre>{out}</pre></>)}
<b>URL: </b><input type="text" value={url} onChange={set_url} size="50"/>
<br/><button onClick={xport}><b>Fetch!</b></button>
</> );
}
```
## Simple Strings
The ["Row Objects" approach](#row-objects) is strongly recommended when trying
to store or recover arrays of row objects.
When the goal is to save an existing Storage, the general representation is an
array of pairs. Consider the following data in Local Storage:
| Key | Value |
|:---:|:----------|
| "b" | "Logical" |
| "n" | "Numeric" |
| "s" | "Textual" |
The natural representation is an array of arrays:
```js
[
[ "b", "Logical" ],
[ "n", "Numeric" ],
[ "s", "Textual" ]
]
```
#### Exporting Storage
In modern browsers, `Object.entries` will generate an array of key/value pairs.
`XLSX.utils.aoa_to_sheet` will interpret that array as a worksheet with 2 cols:
```js
function localStorage_to_ws() {
const aoa = Object.entries(localStorage);
return XLSX.utils.aoa_to_sheet(aoa);
}
```
#### Importing Storage
In the other direction, the worksheet is assumed to store keys in column A and
values in column B. `XLSX.utils.sheet_to_json` with the `header: 1` option
will generate key/value pairs that can be assigned to a storage:
```js
function ws_to_localStorage(ws) {
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1 });
aoa.forEach(([key, val]) => localStorage.setItem(key, val));
}
```

@ -125,23 +125,34 @@ 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.
<details><summary><b>ECMAScript Module Support</b> (click to show)</summary>
<details open><summary><b>ECMAScript Module Support</b> (click to hide)</summary>
:::note Browser Compatibility
ESM is supported in Web Workers in the Chromium family of browsers (including
Chrome and Edge) as well as in browsers powered by WebKit (including Safari).
For support in legacy browsers like Firefox, `importScripts` should be used.
For legacy browsers like Firefox and IE, `importScripts` should be used.
:::
Browser ESM imports require a complete URL including the `.mjs` extension:
```js
import * as XLSX from "https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs";
```
When using modules, the script must be served with the correct MIME type and the
Worker constructor must set the `type` option:
When using Worker ESM, the Worker constructor must set the `type` option:
```js
const worker = new Worker(
url_to_worker_script,
// highlight-next-line
{ type: "module" } // second argument to Worker constructor
);
```
Inline workers additionally require the Blob MIME type `text/javascript`:
```js
const worker_code = `\
@ -158,13 +169,15 @@ const worker = new Worker(
)
),
// highlight-next-line
{type: "module"} // second argument to Worker constructor
{ type: "module" } // second argument to Worker constructor
);
```
</details>
## Downloading a Remote File
## Live Demos
### Downloading a Remote File
:::note fetch in Web Workers
@ -252,7 +265,7 @@ self.addEventListener('message', async(e) => {
</details>
## Creating a Local File
### Creating a Local File
:::caution Writing files from Web Workers
@ -357,7 +370,7 @@ self.addEventListener('message', async(e) => {
</details>
## User-Submitted File
### User-Submitted File
:::note FileReaderSync
@ -375,9 +388,9 @@ sequenceDiagram
actor User
participant Page
participant Worker
User->>Page: click button
User->>Page: submit file
activate Page
Page->>Worker: send URL
Page->>Worker: send pointer
deactivate Page
activate Worker
Note over Worker: fetch file
@ -454,7 +467,7 @@ self.addEventListener('message', (e) => {
</details>
## Streaming Write
### Streaming Write
A more general discussion, including row-oriented processing demos, is included
in the ["Large Datasets"](/docs/demos/stream#browser) demo.

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB