diff --git a/docz/docs/03-demos/09-cloud/02-netsuite.md b/docz/docs/03-demos/09-cloud/02-netsuite.md
index 86317d5..f7c2679 100644
--- a/docz/docs/03-demos/09-cloud/02-netsuite.md
+++ b/docz/docs/03-demos/09-cloud/02-netsuite.md
@@ -25,7 +25,7 @@ This demo was verified by NetSuite consultants in the following deployments:
| `@NScriptType` | `@NApiVersion` | Date |
|:----------------|:---------------|:-----------|
| ScheduledScript | 2.1 | 2023-08-18 |
-| Restlet | 2.1 | 2023-04-20 |
+| Restlet | 2.1 | 2023-10-05 |
| Suitelet | 2.1 | 2023-07-21 |
| MapReduceScript | 2.1 | 2023-07-31 |
diff --git a/docz/docs/03-demos/09-cloud/11-aws.md b/docz/docs/03-demos/09-cloud/11-aws.md
index 647b2b8..83158fb 100644
--- a/docz/docs/03-demos/09-cloud/11-aws.md
+++ b/docz/docs/03-demos/09-cloud/11-aws.md
@@ -311,7 +311,7 @@ Function properties.
17) Select the "Configuration" tab and select "Permissions" in the left sidebar.
-18) Scroll down to "Resource-based policy statements" and ensure that
+18) Scroll down to "Resource-based policy statements" and ensure that
`FunctionURLAllowPublicAccess` is listed.
If no policy statements are defined, select "Add Permission" with the options:
@@ -373,7 +373,7 @@ var s3 = new AWS.S3({
```
-### Reading Data
+### Downloading Data
#### Fetching Files from S3
@@ -425,7 +425,7 @@ stream.on('end', function() {
});
```
-### Writing Data
+### Uploading Data
The SheetJS `write` method[^13] with the option `type: "buffer"` will generate
NodeJS Buffers. `S3#upload` directly accepts these Buffer objects.
diff --git a/docz/docs/03-demos/09-cloud/12-azure.md b/docz/docs/03-demos/09-cloud/12-azure.md
index bf71e7b..5cca6b2 100644
--- a/docz/docs/03-demos/09-cloud/12-azure.md
+++ b/docz/docs/03-demos/09-cloud/12-azure.md
@@ -5,10 +5,26 @@ pagination_next: demos/extensions/index
---
import current from '/version.js';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
-Azure is a Cloud Services platform which includes traditional virtual machine
-support, "Serverless Functions", cloud storage and much more.
+[Azure Cloud Services](https://azure.microsoft.com/) is a Cloud Services
+platform which includes traditional virtual machine support, "Serverless
+Functions" and cloud storage.
+
+[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
+data from spreadsheets.
+
+This demo explores two key AWS offerings:
+
+- ["Azure Functions"](#azure-functions) ("Lambda") explores the serverless
+ computing offering. The demo creates a JavaScript function that can process
+ user-submitted files and generate spreadsheets.
+
+- ["Blob Storage"](#blob-storage) explores the cloud storage offering. The demo
+ uses the NodeJS connection library to read spreadsheets from storage and write
+ spreadsheets back to cloud storage.
:::caution pass
@@ -17,22 +33,77 @@ will be available in the future.
:::
-This demo focuses on two key offerings: cloud storage ("Azure Blob Storage")
-and the "Serverless Function" platform ("Azure Functions").
-
:::note
-This demo was last tested on 2023 April 29.
+This demo was last tested on 2023 October 06.
:::
+## Telemetry
+
+:::warning Telemetry
+
+**Each command-line tool related to Azure embeds telemetry.**
+
+Azure tools embed telemetry without proper disclaimer.
+
+:::
+
+It is strongly recommended to disable telemetry before working with Azure.
+
+#### Azure Functions Core Tools
+
+Azure Functions Core Tools (`func`) telemetry is controlled through the
+`FUNCTIONS_CORE_TOOLS_TELEMETRY_OPTOUT` environment variable.
+
+
+
+
+Add the following line to `.profile`, `.bashrc` and `.zshrc`:
+
+```bash
+export FUNCTIONS_CORE_TOOLS_TELEMETRY_OPTOUT=1
+```
+
+Close and restart the Terminal to load the changes.
+
+
+
+
+Type `env` in the search bar and select "Edit the system environment variables".
+
+In the new window, click the "Environment Variables..." button.
+
+In the new window, look for the "System variables" section and click "New..."
+
+Set the "Variable name" to `FUNCTIONS_CORE_TOOLS_TELEMETRY_OPTOUT` and the value
+to `1`.
+
+Click "OK" in each window (3 windows) and restart your computer.
+
+
+
+
+#### Azure CLI
+
+Azure CLI (`az`) telemetry can be disabled using a subcommand (after installing
+the CLI tool)[^1]:
+
+```bash
+az configure -d collect_telemetry=false
+```
+
## Azure Functions
+The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
+required in Azure Functions that use the NodeJS runtime.
+
This discussion focuses on the "HTTP Trigger" function type.
-:::info pass
+:::note pass
-To enable binary data processing, a setting must be changed in `function.json`:
+In earlier tests, to enable binary data processing, `function.json` required a
+`dataType` option:
```json title="function.json"
{
@@ -45,150 +116,295 @@ To enable binary data processing, a setting must be changed in `function.json`:
"name": "req",
```
+In the most recent test, the template did not create a `function.json` and the
+option was not required.
+
:::
### Reading Data
-`formidable` expects a stream and Azure does not present one. It can be made:
+Using `@azure/functions`, the handler callback receives a `Request` object. With
+standard JS operations, the file can be pulled into an `ArrayBuffer` object.
+
+The SheetJS `read` method[^2] can read the `ArrayBuffer` objects and generate
+SheetJS workbook objects[^3] which can be processed with other API functions.
+
+For example, a handler can use `sheet_to_csv`[^4] to generate CSV text:
```js
+const { Blob } = require('buffer');
+const { app } = require('@azure/functions');
const XLSX = require('xlsx');
-const formidable = require('formidable');
-const Readable = require('stream').Readable;
-/* formidable expects the request object to be a stream */
-const streamify = (req) => {
- if(typeof req.on !== 'undefined') return req;
- const s = new Readable();
- s._read = ()=>{};
- s.push(Buffer.from(req.body));
- s.push(null);
- Object.assign(s, req);
- return s;
-};
+app.http('SheetJSAzure', {
+ methods: ['POST'],
+ handler: async (req, context) => {
+ /* grab the file at form key `upload` */
+ const formData = await req.formData();
+ const f = formData.get("upload");
-module.exports = (context, req) => {
- const form = new formidable.IncomingForm();
- form.parse(streamify(req), (err, fields, files) => {
- /* grab the first file */
- var f = files["upload"];
- if(!f) {
- context.res = { status: 400, body: "Must submit a file for processing!" };
- } else {
- /* file is stored in a temp directory, so we can point to that and read it */
- const wb = XLSX.read(f.filepath, {type:"file"});
+ if(!(f instanceof Blob)) return { status: 400, body: "Must submit a file" };
- /* generate CSV from first sheet */
- const csv = XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
- context.res = { status: 200, body: csv };
- }
- context.done();
- });
-}
+ /* parse file */
+ const ab = await f.arrayBuffer();
+ const wb = XLSX.read(ab);
+
+ /* generate CSV from first sheet */
+ const ws = wb.Sheets[wb.SheetNames[0]];
+ const csv = XLSX.utils.sheet_to_csv(ws);
+ return { status: 200, body: csv };
+ }
+});
```
### Writing Data
-The `body` property can be a Buffer, like those generated by `XLSX.write`:
+The SheetJS `write` method[^5] with the option `type: "buffer"` will generate
+NodeJS buffers which can be sent in the callback handler response.
+
+The following example generates a sample worksheet using the `aoa_to_sheet`[^6]
+method, generates a sample workbook using worksheet helper methods[^7], writes
+the workbook to XLSX format in a Buffer, and sends the Buffer in the response:
```js
+const { app } = require('@azure/functions');
const XLSX = require('xlsx');
-module.exports = (context, req) => {
- // generate XLSX file in a Buffer
- var ws = XLSX.utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
- var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Data");
- // highlight-next-line
- var buf = XLSX.write(wb, {type: "buffer", bookType: "xlsx"});
- // Set the body and Content-Disposition header
- // highlight-start
- context.res = {
- status: 200,
- headers: { "Content-Disposition": `attachment; filename="SheetJSAzure.xlsx";` },
- body: buf
- };
- // highlight-end
- context.done();
-};
+app.http('SheetJSAzure', {
+ methods: ['GET'],
+ handler: async (req, context) => {
+ /* generate sample worksheet */
+ var ws = XLSX.utils.aoa_to_sheet(["SheetJS".split(""), [5, 4, 3, 3, 7, 9, 5]]);
+ /* generate workbook */
+ var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Data");
+ /* write to XLSX, returning a NodeJS Buffer */
+ var buf = XLSX.write(wb, { type: "buffer", bookType: "xlsx" });
+ /* send Buffer to client */
+ return {
+ status: 200,
+ /* Content-Disposition header */
+ headers: { "Content-Disposition": `attachment; filename="SheetJSAzure.xlsx";` },
+ /* data */
+ body: buf
+ };
+ }
+});
```
-### Demo
+### Functions Demo
-Complete Example (click to show)
+:::note pass
-0) Review the quick start for JavaScript on Azure Functions. This involves
-installing the Azure Functions Core Tools and other dependencies.
+At the time of writing, the Azure Free Tier included an allowance of 1 million
+free requests per month.
-1) Create a new project and install dependencies:
+:::
+
+0) If you do not have an account, create a new Azure free tier account[^8].
+
+#### Local Setup
+
+1) [Disable Azure Functions Core Tools Telemetry](#azure-functions-core-tools).
+
+2) Install the CLI tool using npm:
+
+```bash
+npm i -g azure-functions-core-tools@4 --unsafe-perm true
+```
+
+:::note pass
+
+On macOS and Linux, `sudo` may be required:
+
+```bash
+sudo npm i -g azure-functions-core-tools@4 --unsafe-perm true
+```
+
+:::
+
+3) Install [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli)
+
+4) Disable Azure CLI telemetry:
+
+```bash
+az configure -d collect_telemetry=false
+```
+
+#### Start Project
+
+5) Create a new JavaScript HTTP Trigger project:
+
+```bash
+mkdir SheetJSAzure
+cd SheetJSAzure
+func new --template httpTrigger --language JavaScript --name SheetJSAzure
+```
+
+:::warning pass
+
+When the demo was last tested, the stock TypeScript template did not work.
+
+**This is a bug in the Azure Functions Core Tools**
+
+Until the bugs are resolved, JavaScript should be preferred over TypeScript.
+
+:::
+
+6) Start the local server:
+
+```bash
+npm start
+```
+
+7) While the server is running, open a new terminal window and make a request:
+
+```bash
+curl -L http://localhost:7071/api/SheetJSAzure
+```
+
+The terminal should display `Hello, world!`
+
+#### Add SheetJS
+
+8) Install the SheetJS NodeJS module:
{`\
-func init sheetjs-azure --worker-runtime node --language javascript
-cd sheetjs-azure
-npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz formidable`}
+npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
-2) Create a new "HTTP Trigger" function:
+9) Download [the sample script](pathname:///azure/index.js):
```bash
-func new --template "Http Trigger" --name SheetJSAzure
+curl -L -o src/functions/SheetJSAzure.js https://docs.sheetjs.com/azure/index.js
```
-3) Edit `SheetJSAzure/function.json` to add the `dataType: "binary"` property:
+#### Local Test
-```js title="SheetJSAzure/function.json"
- "direction": "in",
-// highlight-next-line
- "dataType": "binary",
- "name": "req",
-```
-
-4) Download [`SheetJSAzure/index.js`](pathname:///aws/index.js):
+10) Stop and restart the dev server:
```bash
-curl -L -o SheetJSAzure/index.js https://docs.sheetjs.com/azure/index.js
+npm start
```
-5) Test locally with `npm start`
-
-To test uploads, download and run:
+11) In a separate terminal window, download
+and make a POST request to the dev server:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -X POST -F "upload=@pres.numbers" http://localhost:7071/api/SheetJSAzure
```
-To test downloads, access `http://localhost:7071/api/SheetJSAzure` and download
-the generated file. Confirm it is a valid file.
+If the test succeeded, the terminal will print CSV rows from the test file data.
-6) Deploy to Azure. Replace `NAME_OF_FUNCTION_APP` with the name:
+12) Open a web browser and access `http://localhost:7071/api/SheetJSAzure` .
-```bash
-func azure functionapp publish NAME_OF_FUNCTION_APP
+If the test succeeded, the browser will attempt to download `SheetJSAzure.xlsx`.
+Open in Excel or another spreadsheet editor to confirm the file is valid.
+
+#### Create Remote Function
+
+13) Sign into the [Azure Portal](https://portal.azure.com/#home)
+
+14) Type "Function App" in the top search box and click "Function App"
+
+15) Click "+ Create"
+
+16) Select the following options:
+
+- Type a memorable "Function Name" ("sheetjsazure" when last tested)
+
+- "Do you want to deploy code or container image?": select "Code"
+
+- "Runtime stack": select NodeJS
+
+- "Hosting options and plans": "Consumption (Serverless)"
+
+17) Click "Review + create", then click "Create" to create the function.
+
+The page will display a status message
+
+> ... Deployment is in progress
+
+When the resources are configured, the status will change to
+
+> Your deployment is complete
+
+18) Click "Go to Resource".
+
+19) Take note of the URL from the table
+
+#### Deploy to Azure
+
+20) Sign into Azure:
+
+```
+az login
```
-Get the function URL and test using the same sequence as in step 5.
+The login flow resumes in the browser.
-
+21) Deploy to Azure. Replace `FUNCTION_NAME` with the name from Step 16:
-## Azure Blob Storage
+```bash
+func azure functionapp publish FUNCTION_NAME
+```
+
+After publishing, the process will print the "Invoke url":
+
+```
+Functions in sheetjsazure:
+ SheetJSAzure - [httpTrigger]
+// highlight-next-line
+ Invoke url: https://sheetjsazure.azurewebsites.net/api/sheetjsazure
+```
+
+Take note of that URL.
+
+#### Remote Test
+
+
+22) In a separate terminal window, download
+and make a POST request to the production server. Replace `FUNCTION_URL` with
+the Invoke URL from Step 21:
+
+```bash
+curl -LO https://sheetjs.com/pres.numbers
+curl -X POST -F "upload=@pres.numbers" FUNCTION_URL
+```
+
+If the test succeeded, the terminal will print CSV rows from the test file data.
+
+23) Open a web browser and access the Invoke URL from Step 21.
+
+If the test succeeded, the browser will attempt to download `SheetJSAzure.xlsx`.
+Open in Excel or another spreadsheet editor to confirm the file is valid.
+
+## Blob Storage
The main module for Azure Blob Storage is `@azure/storage-blob`. This example
was tested using the "Connection String" authentication method. The strings
are found in the Azure Portal under "Access Keys" for the storage account.
-### Reading Data
+### Downloading Data
The `BlobClient#download` method returns a Stream. After collecting into a
-Buffer, `XLSX.read` can parse the data:
+Buffer, the SheetJS `read` method[^9] can parse the data into a workbook[^10].
+
+The following demo uses the `sheet_to_csv`[^11] utility function to display the
+contents of a file in Azure Blob Storage:
```js title="SheetJSReadFromAzure.mjs"
import { BlobServiceClient } from "@azure/storage-blob";
import { read, utils } from "xlsx";
/* replace these constants */
+// highlight-start
const connStr = "";
const containerName = "";
-const blobName = "";
+// highlight-end
+
+/* Blob name */
+const blobName = "SheetJSBloblobber.xlsx";
/* get a readable stream*/
const blobServiceClient = BlobServiceClient.fromConnectionString(connStr);
@@ -207,18 +423,27 @@ const wb = read(downloaded);
console.log(utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
```
-### Writing Data
+### Uploading Data
-`BlockBlobClient#upload` directly accepts a Buffer:
+The SheetJS `write` method[^12] with the option `type: "buffer"` will generate
+NodeJS buffers which can be uploaded with `BlockBlobClient#upload`.
+
+The following example generates a sample worksheet using the `aoa_to_sheet`[^13]
+method, generates a sample workbook using worksheet helper methods[^14], writes
+the workbook to XLSX format in a Buffer, and sends the Buffer in the response:
```js title="SheetJSWriteToAzure.mjs"
import { BlobServiceClient } from "@azure/storage-blob";
import { write, utils } from "xlsx";
/* replace these constants */
+// highlight-start
const connStr = "";
const containerName = "";
-const blobName = "";
+// highlight-end
+
+/* Blob name */
+const blobName = "SheetJSBloblobber.xlsx";
/* Create a simple workbook and write XLSX to buffer */
const ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
@@ -231,3 +456,165 @@ const containerClient = blobServiceClient.getContainerClient(containerName);
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const uploadBlobResponse = await blockBlobClient.upload(buf, buf.length);
```
+
+### Blob Demo
+
+:::note pass
+
+At the time of writing, new Azure accounts were granted a 12-month trial of Blob
+Storage. The trial includes 5GB of "Locally-redundant storage" with 20,000 read
+requests and 2000 write requests per month.
+
+:::
+
+0) If you do not have an account, create a new Azure free tier account[^8].
+
+#### Storage Account Setup
+
+1) Sign into the [Azure Portal](https://portal.azure.com/#home)
+
+2) Type "Storage" in the top search box and click "Storage accounts"
+
+3) Click "+ Create"
+
+4) Select the following options:
+
+- Type a memorable "Storage account name" ("sheetjstorage" when last tested)
+
+- "Redundancy": select LRS (Locally-redundant storage)
+
+- "Hosting options and plans": "Consumption (Serverless)"
+
+5) Click "Review", then click "Create" to create the storage.
+
+The page will display a status message
+
+> ... Deployment is in progress
+
+When the resources are configured, the status will change to
+
+> Your deployment is complete
+
+6) Click "Go to Resource".
+
+#### Access Keys
+
+7) Click "Access keys" in the left sidebar (under "Security + networking")
+
+8) Look for the "Connection string" title under "key1". In the row below the
+title, click "Show" to reveal the key. Click the copy icon or manually copy the
+key, storing it in a safe place.
+
+#### Container Setup
+
+9) Click "Containers" in the left sidebar.
+
+10) Click "+ Container"
+
+11) Select the following options:
+
+- Type a memorable "Name" ("sheetjs-container" when last tested)
+
+12) Click "Create" to create the container.
+
+#### Project Setup
+
+13) Create a new project folder:
+
+```bash
+mkdir SheetJSBlob
+cd SheetJSBlob
+npm init -y
+```
+
+14) Install dependencies:
+
+{`\
+npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @azure/storage-blob`}
+
+
+14) Copy the [`SheetJSReadFromAzure.mjs` code block](#downloading-data) and save
+to `SheetJSReadFromAzure.mjs`.
+
+15) Copy the [`SheetJSWriteToAzure.mjs` code block](#uploading-data) and save
+to `SheetJSWriteToAzure.mjs`.
+
+16) Edit both `SheetJSReadFromAzure.mjs` and `SheetJSWriteToAzure.mjs`:
+
+- Replace the `connStr` value with the connection string from Step 8
+- Replace the `containerName` value with the container name from Step 11
+
+#### Test
+
+:::note pass
+
+The write demo creates a simple workbook, generates a NodeJS buffer, and uploads
+the buffer to a file named `SheetJSBloblobber.xlsx` on Azure Blob Storage.
+
+The read demo fetches `SheetJSBloblobber.xlsx` and displays the data.
+
+```
+ | A | B | C | D | E | F | G |
+---+---|---|---|---|---|---|---|
+ 1 | S | h | e | e | t | J | S |
+ 2 | 5 | 4 | 3 | 3 | 7 | 9 | 5 |
+```
+
+:::
+
+17) Run the write test:
+
+```bash
+node SheetJSWriteToAzure.mjs
+```
+
+This will write the file `SheetJSBloblobber.xlsx` to the container.
+
+18) Run the read test:
+
+```bash
+node SheetJSReadFromAzure.mjs
+```
+
+It will fetch the file created in the previous step and display CSV rows.
+
+```
+S,h,e,e,t,J,S
+5,4,3,3,7,9,5
+```
+
+19) Sign into the [Azure Portal](https://portal.azure.com/#home)
+
+20) Type "Storage" in the top search box and click "Storage accounts"
+
+21) Click on the name of the storage
+
+22) In the middle column, click "Containers". It will be under "Data storage".
+
+23) Click on the name of the container in the table
+
+24) Verify that the table shows `SheetJSBloblobber.xlsx`:
+
+![SheetJSBloblobber.xlsx in the container](pathname:///azure/bloblobber.png)
+
+25) Click on the name `SheetJSBloblobber.xlsx`.
+
+26) In the right pane, click "Download".
+
+The downloaded file is the raw file stored in Azure Blob Storage. To confirm it
+is valid, open the file in Excel or another spreadsheet editor.
+
+[^1]: The platform-specific installers are available at
+[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
+[^3]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
+[^4]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
+[^5]: See [`write` in "Writing Files"](/docs/api/write-options)
+[^6]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
+[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
+[^8]: Registering for a free account [on the Azure Free Tier](https://azure.microsoft.com/en-us/free) requires a valid phone number and a valid credit card.
+[^9]: See [`read` in "Reading Files"](/docs/api/parse-options)
+[^10]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
+[^11]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
+[^12]: See [`write` in "Writing Files"](/docs/api/write-options)
+[^13]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
+[^14]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
diff --git a/docz/docs/index.md b/docz/docs/index.md
index 3ce90e4..a13eaf7 100644
--- a/docz/docs/index.md
+++ b/docz/docs/index.md
@@ -5,6 +5,8 @@ title: Overview
---
import current from '/version.js';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
# SheetJS CE
@@ -27,6 +29,9 @@ run entirely in the web browser.
How to add to your site (click to show)
+
+
+
1) Make sure your table has an ID:
```html
@@ -45,7 +50,7 @@ run entirely in the web browser.
```
-4) Add an event handler for the `click` event to create a workbook and download:
+4) Add an event handler for the `click` event to export table data to XLSX:
```html
```
+
+
+
+:::note pass
+
+This example assumes you have an existing project with an HTML TABLE element:
+
+```jsx title="Sample Component"
+function App() {
+ return ( <>
+
+ > )
+}
+export default App;
+```
+
+If you are starting from scratch, create a new ViteJS + ReactJS project:
+
+```bash
+npm create vite@latest -- sheetjs-react --template react --default
+cd sheetjs-react
+npm install
+npm run dev
+```
+
+Replace `src/App.jsx` with the sample component.
+
+:::
+
+1) Install the SheetJS library using a package manager:
+
+
+
+{`\
+npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
+
+
+
+{`\
+pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
+
+
+
+{`\
+yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
+
+
+
+
+2) Ensure that your component script imports `useRef` from the `react` library:
+
+```js
+import { useRef } from "react";
+```
+
+3) Add the following line at the top of your component script:
+
+```js
+import { utils, writeFileXLSX } from "xlsx";
+```
+
+4) Create a ref in the body of your function component:
+
+```jsx
+function App() {
+// highlight-next-line
+ const tbl = useRef(null);
+ // ...
+```
+
+5) Attach the ref to the table element:
+
+```jsx
+function App() {
+ // ...
+ return (
+ {/*...*/}
+// highlight-next-line
+
+ {/*...*/}
+```
+
+6) Add a button with a click handler that will export table data to XLSX:
+
+```jsx
+function App() {
+ // ...
+ return (
+ {/*...*/}
+// highlight-start
+
+// highlight-end
+ {/*...*/}
+```
+
+
+
+
How to automate with NodeJS (click to show)
@@ -74,7 +190,7 @@ using the `puppeteer` and `playwright` browser automation frameworks.
function Table2XLSX(props) {
/* Callback invoked when the button is clicked */
- const xport = React.useCallback(async () => {
+ const xport = React.useCallback(() => {
/* Create worksheet from HTML DOM TABLE */
const table = document.getElementById("Table2XLSX");
const wb = XLSX.utils.table_to_book(table);
diff --git a/docz/docusaurus.config.js b/docz/docusaurus.config.js
index c283b59..bb7c94c 100644
--- a/docz/docusaurus.config.js
+++ b/docz/docusaurus.config.js
@@ -202,6 +202,7 @@ const config = {
{ from: '/docs/demos/hosting/dropbox', to: '/docs/demos/cloud/dropbox/' },
{ from: '/docs/demos/hosting/github', to: '/docs/demos/cloud/github/' },
/* data */
+ { from: '/docs/getting-started/demos/database', to: '/docs/demos/data/' },
{ from: '/docs/demos/database', to: '/docs/demos/data/' },
{ from: '/docs/demos/nosql', to: '/docs/demos/data/' },
{ from: '/docs/getting-started/demos/nosql', to: '/docs/demos/data/' },
diff --git a/docz/static/azure/bloblobber.png b/docz/static/azure/bloblobber.png
new file mode 100644
index 0000000..182ef1d
Binary files /dev/null and b/docz/static/azure/bloblobber.png differ
diff --git a/docz/static/azure/index.js b/docz/static/azure/index.js
index b20ed83..38aa42e 100644
--- a/docz/static/azure/index.js
+++ b/docz/static/azure/index.js
@@ -1,49 +1,38 @@
/* sheetjs (C) SheetJS -- https://sheetjs.com */
+const { Blob } = require('buffer');
+const { app } = require('@azure/functions');
const XLSX = require('xlsx');
-const formidable = require('formidable');
-const Readable = require('stream').Readable;
-/* formidable expects the request object to be a stream */
-const streamify = (req) => {
- if(typeof req.on !== 'undefined') return req;
- const s = new Readable();
- s._read = ()=>{};
- s.push(Buffer.from(req.body));
- s.push(null);
- Object.assign(s, req);
- return s;
-};
+app.http('SheetJSAzure', {
+ methods: ['GET', 'POST'],
+ authLevel: 'anonymous',
+ handler: async (req, context) => {
+ if (req.method == "POST") {
+ /* grab the file at form key `upload` */
+ const formData = await req.formData();
+ const f = formData.get("upload");
-module.exports = (context, req) => {
- if(req.method == "POST") {
- const form = new formidable.IncomingForm();
- form.parse(streamify(req), (err, fields, files) => {
- /* grab the first file */
- var f = files["upload"];
- if(!f) {
- context.res = { status: 400, body: "Must submit a file for processing!" };
+ if (!f || !(f instanceof Blob)) {
+ return { status: 400, body: "Must submit a file for processing!" };
} else {
- /* file is stored in a temp directory, so we can point to that and read it */
- const wb = XLSX.read(f.filepath, {type:"file"});
+ /* parse file */
+ const ab = await f.arrayBuffer();
+ const wb = XLSX.read(ab);
/* generate CSV from first sheet */
const csv = XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
- context.res = { status: 200, body: csv };
+ return { status: 200, body: csv };
}
- context.done();
- });
- } else if(req.method == "GET") {
- var ws = XLSX.utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
- var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Data");
- var buf = XLSX.write(wb, {type: "buffer", bookType: "xlsx"});
- context.res = {
- status: 200,
- headers: { "Content-Disposition": `attachment; filename="SheetJSAzure.xlsx";` },
- body: buf
- };
- context.done();
- } else {
- context.res = { status: 500, body: `Unsupported method ${req.method}` };
- context.done();
+
+ } else if (req.method == "GET") {
+ var ws = XLSX.utils.aoa_to_sheet(["SheetJS".split(""), [5, 4, 3, 3, 7, 9, 5]]);
+ var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Data");
+ var buf = XLSX.write(wb, { type: "buffer", bookType: "xlsx" });
+ return {
+ status: 200,
+ headers: { "Content-Disposition": `attachment; filename="SheetJSAzure.xlsx";` },
+ body: buf
+ };
+ } else return { status: 500, body: `Unsupported method ${req.method}` };
}
-};
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/docz/static/sqlite/chinook.sql b/docz/static/sqlite/chinook.sql
index 4d35770..2680dc6 100644
--- a/docz/static/sqlite/chinook.sql
+++ b/docz/static/sqlite/chinook.sql
@@ -1,11 +1,11 @@
/*
Licensing Note:
- At the time this extract was made (2023 May 28), the linked license page
+ At the time this snapshot was taken, the linked license page
http://www.codeplex.com/ChinookDatabase/license
- was unavailable (Microsoft shuttered CodePlex).
+ was unavailable (Microsoft shuttered CodePlex in 2021)
archive.org has a snapshot of the project license page: