gsheet-api

This commit is contained in:
SheetJS 2023-09-18 02:44:33 -04:00
parent 4438bd86cd
commit 8914e8f714
11 changed files with 695 additions and 343 deletions

View File

@ -9,12 +9,11 @@ import CodeBlock from '@theme/CodeBlock';
:::note
This demo was last tested on 2023 April 18 with `react-data-grid 7.0.0-beta.28`,
`create-react-app` 5.0.1 and React 18.2.0.
This demo was last tested on 2023 September 17 with `react-data-grid` version
`7.0.0-beta.39` and React 18.2.0.
:::
The demo creates a site that looks like the screenshot below:
![react-data-grid screenshot](pathname:///rdg/rdg1.png)
@ -117,7 +116,7 @@ cd sheetjs-rdg
2) Install dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid`}
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.39`}
</CodeBlock>
3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`.
@ -126,5 +125,15 @@ npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-
curl -L -o src/App.tsx https://docs.sheetjs.com/rdg/App.tsx
```
4) run `npm start`. When you load the page in the browser, it will attempt to
fetch <https://sheetjs.com/pres.numbers> and load the data.
4) Start the development server:
```bash
npm start
```
#### Testing
5) When the page loads, it will fetch <https://sheetjs.com/pres.numbers>, parse
with SheetJS, and load the data in the data grid.
6) Click one of the "export" buttons to export the grid data to file.

View File

@ -1,5 +1,7 @@
---
title: Quasar
title: Data in Quasar Apps
sidebar_label: Quasar
description: Build data-intensive mobile apps with Quasar and Cordova. Seamlessly integrate spreadsheets into your app using SheetJS. Let data in your Excel spreadsheets shine.
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 3
@ -10,10 +12,17 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
from the main entrypoint or any script in the project.
[Quasar](https://quasar.dev/) is a VueJS framework for building iOS and Android
apps with the Cordova platform.
The "Complete Example" creates an app that looks like the screenshots below:
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Quasar and SheetJS to process data and generate spreadsheets.
We'll explore how to load SheetJS in an Quasar app and use Quasar and Cordova
features to extract data from, and write data to, spreadsheets on the device.
The ["Demo"](#demo) creates an app that looks like the screenshots below:
<table><thead><tr>
<th><a href="#demo">iOS</a></th>
@ -30,10 +39,14 @@ The "Complete Example" creates an app that looks like the screenshots below:
### Integration Details
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
from the main entrypoint or any script in the project.
This demo will use the Quasar ViteJS starter project with VueJS and Cordova.
The starter places the backing Cordova project in the `src-cordova` folder.
The complete solution uses `cordova-plugin-file` for file operations. It can
be installed like any other Cordova plugin:
be installed from the Cordova folder:
```bash
cd src-cordova
@ -41,15 +54,21 @@ cordova plugin add cordova-plugin-file
cd ..
```
#### Reading data
### Reading data
The `q-file` component presents an API reminiscent of File Input elements:
The QFile[^1] component presents an API reminiscent of File Input elements:
```html
<q-file label="Load File" filled label-color="orange" @input="updateFile"/>
```
When binding to the `input` element, the callback receives an `Event` object:
When binding to the `input` element, the callback receives an `Event` object.
Using standard DOM operations, the file data can be pulled into an `ArrayBuffer`
and parsed using the SheetJS `read` method[^2]. `read` returns a workbook[^3]
object that holds data and metadata for each worksheet.
This snippet reads a workbook, pulls the first worksheet, generates an array of
objects using the SheetJS `sheet_to_json`[^4] method, and updates state:
```ts
import { read } from 'xlsx';
@ -74,14 +93,33 @@ async function updateFile(v) { try {
#### Writing data
The API is shaped like the File and Directory Entries API. For clarity, since
the code is a "pyramid of doom", the error handlers are omitted:
Starting from an array of objects, the SheetJS `json_to_sheet` method[^5]
generates a SheetJS worksheet object. The `book_append_sheet` and `book_new`
helper functions[^6] create a SheetJS workbook object that can be exported:
```ts
```js
import { utils } from 'xlsx';
// assuming `todos` is a VueJS ref whose value is an array of objects
const ws = utils.json_to_sheet(todos.value);
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "SheetJSQuasar");
```
The SheetJS `write` function[^7] with the option `type: "buffer"` will generate
`Uint8Array` objects that can be converted to blobs and exported:
```js
import { write } from 'xlsx';
// on iOS and android, `XLSX.write` with type "buffer" returns a `Uint8Array`
const u8: Uint8Array = write(wb, {bookType: "xlsx", type: "buffer"});
```
The `cordova-plugin-file` API writes the data to the filesystem. The code uses
the File and Directory Entries API[^8]:
```ts
// Request filesystem access for persistent storage
window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
// Request a handle to "SheetJSQuasar.xlsx", making a new file if necessary
@ -107,12 +145,11 @@ window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
:::note
This demo was tested on an Intel Mac on 2023 April 08. `create-quasar@1.1.2`
was installed during app creation. The app used Quasar version `2.11.10`.
The Android demo was last tested on 2023 September 18 with Quasar `2.12.7` on an
emulated Pixel 3 + Android 13 ("Tiramisu") API 33.
The iOS simulator runs iOS 16.2 on an iPhone 14 Pro Max.
The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
The iOS demo was last tested on 2023 September 18 with Quasar `2.12.7` on an
emulated iPhone SE (3rd generation) + iOS 16.4.
:::
@ -196,20 +233,38 @@ Return to the project directory:
cd ..
```
4) Start the development server:
11) Enable file sharing and make the documents folder visible in the iOS app.
The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist`:
```xml title="src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist (add to file)"
<plist version="1.0">
<dict>
<!-- highlight-start -->
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<!-- highlight-end -->
<key>CFBundleDevelopmentRegion</key>
```
(The root element of the document is `plist` and it contains one `dict` child)
5) Start the development server:
```bash
quasar dev -m ios
```
:::caution
:::caution pass
If the app is blank or not refreshing, delete the app and close the simulator,
then restart the development process.
:::
5) Add the Dialog plugin to `quasar.config.js`:
6) Add the Dialog plugin to `quasar.config.js`:
```js title="quasar.config.js"
framework: {
@ -221,7 +276,7 @@ then restart the development process.
},
```
6) In the template section of `src/pages/IndexPage.vue`, replace the example
7) In the template section of `src/pages/IndexPage.vue`, replace the example
with a Table, Save button and Load file picker component:
```html title="src/pages/IndexPage.vue"
@ -266,7 +321,7 @@ then restart the development process.
:::
7) Wire up the `updateFile` function:
8) Wire up the `updateFile` function:
```ts title="src/pages/IndexPage.vue"
import { defineComponent, ref } from 'vue';
@ -315,7 +370,7 @@ To test that reading works:
Once selected, the screen should refresh with new contents.
8) Wire up the `saveFile` function:
9) Wire up the `saveFile` function:
```ts title="src/pages/IndexPage.vue"
function saveFile() {
@ -401,7 +456,7 @@ id,content
**Android**
9) Create the Android project:
10) Create the Android project:
```bash
cd src-cordova
@ -409,24 +464,12 @@ cordova platform add android
cd ..
```
10) Start the simulator:
11) Start the simulator:
```bash
quasar dev -m android
```
:::note
In local testing, the Quasar build process threw an error:
```
java.lang.IllegalArgumentException: Unsupported class file major version 63
```
This was resolved by rolling back to Java 1.8
:::
To test that reading works:
- Click and drag `pres.numbers` from a Finder window into the simulator.
@ -442,3 +485,12 @@ To test that writing works:
adb exec-out run-as org.sheetjs.quasar cat files/files/SheetJSQuasar.xlsx > /tmp/SheetJSQuasar.xlsx
npx xlsx-cli /tmp/SheetJSQuasar.xlsx
```
[^1]: See ["File Picker"](https://quasar.dev/vue-components/file-picker) in the Quasar documentation.
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^3]: See ["SheetJS Data Model"](/docs/csf/) for more details on workbooks, worksheets, and other concepts.
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^5]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^7]: See [`write` in "Writing Files"](/docs/api/write-options)
[^8]: See [`requestFileSystem`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestFileSystem) in the MDN Web Docs for more details.

View File

@ -183,7 +183,9 @@ The following Web APIs are featured in separate demos:
<a href={item.href}>{item.label}</a>{item.customProps?.summary && (" - " + item.customProps.summary)}
</li>);
})}
<li><a href="/docs/demos/local/indexeddb">IndexedDB API</a></li></ul>
<li><a href="/docs/demos/local/storageapi">Local Storage API</a></li>
<li><a href="/docs/demos/local/indexeddb">IndexedDB API</a></li>
</ul>
### SQL Databases

View File

@ -1,9 +1,9 @@
---
title: Local Storage API
pagination_prev: demos/desktop/index
pagination_next: demos/local/index
pagination_prev: demos/data/index
pagination_next: demos/cloud/index
sidebar_custom_props:
type: web
summary: Reading and writing data in an in-browser Key-Value store
---
The Storage API, encompassing `localStorage` and `sessionStorage`, describes
@ -14,6 +14,17 @@ 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
:::note
Each browser demo was tested in the following environments:
| Browser | Date |
|:------------|:-----------|
| Chrome 116 | 2023-09-17 |
| Safari 16.6 | 2023-09-17 |
:::
## Row Objects
Consider the following array of objects of data:
@ -70,12 +81,6 @@ function localStorage_to_sheet() {
### Live Demo
:::note
This demo was last tested on 2023 April 09.
:::
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.
@ -84,13 +89,15 @@ After saving the exported file, the Local Storage can be inspected in the
![Local Storage view in Developer Tools](pathname:///storageapi/lstorage.png)
:::caution
:::caution pass
This example is for illustration purposes. If array of objects is available, it
is strongly recommended to convert that array to a worksheet directly.
:::
<details><summary><b>Live Demo</b> (click to show)</summary>
```jsx live
function SheetJStorage() {
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
@ -129,6 +136,8 @@ function SheetJStorage() {
}
```
</details>
## Simple Strings
The ["Row Objects" approach](#row-objects) is strongly recommended when trying
@ -155,7 +164,7 @@ The natural representation is an array of arrays:
#### Exporting Storage
:::note
:::note pass
Web Storage iteration order is not defined. By using indices as keys, the row
objects approach has an ordering. That does not apply to the general case.
@ -187,15 +196,11 @@ function ws_to_localStorage(ws) {
### Live Demo
:::note
This demo was last tested on 2023 April 09.
:::
This example fills `localStorage` with 10 random keys and 10 random values,
generates a worksheet from the data and writes to a new file.
<details><summary><b>Live Demo</b> (click to show)</summary>
```jsx live
function SheetJSRandomStorage() {
const [out, setOut] = React.useState("");
@ -231,3 +236,5 @@ function SheetJSRandomStorage() {
</> );
}
```
</details>

View File

@ -3,7 +3,7 @@ title: IndexedDB API
pagination_prev: demos/desktop/index
pagination_next: demos/local/index
sidebar_custom_props:
summary: Reading and writing data in browser storage
summary: Reading and writing data in an in-browser NoSQL database
---
<head>

View File

@ -9,16 +9,7 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
This demo uses `node-google-spreadsheet` to interact with Google Sheets v4 API.
:::caution
Google Sheets deprecates APIs quickly and there is no guarantee that the
referenced API version will be available in the future.
:::
:::note
:::note pass
This demo focuses on external data processing. For Google Apps Script custom
functions, [the "Google Sheets" extension demo](/docs/demos/extensions/gsheet)
@ -26,127 +17,123 @@ covers Apps Script integration.
:::
## Initial Configuration
[Google Sheets](https://google.com/sheets/about/) is a collaborative spreadsheet
service with powerful external APIs for automation.
Install the dependencies:
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet@3.3.0`}
</CodeBlock>
This demo uses SheetJS to properly exchange data with spreadsheet files. We'll
explore how to use NodeJS integration libraries and SheetJS in three data flows:
The library README has a [guide](https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication)
for configuring a service worker with write access to the document. Following
the service worker guide, the JSON key should be saved to `key.json`.
- "Importing data": Data in a NUMBERS spreadsheet will be parsed using SheetJS
libraries and written to a Google Sheets Document
The following helper function attempts to authenticate and access the specified
sheet by ID. The code should be copied and saved to `common.js`:
- "Exporting data": Data in Google Sheets will be pulled into arrays of objects.
A workbook will be assembled and exported to Excel Binary workbooks (XLSB).
<details><summary><b>Code</b> (click to show)</summary>
- "Exporting files": SheetJS libraries will read XLSX and ODS files exported by
Google Sheets and generate CSV rows from every worksheet.
```js title=common.js
const fs = require("fs");
const { GoogleSpreadsheet } = require('google-spreadsheet');
:::warning pass
module.exports = async(ID) => {
/* get credentials */
const creds = JSON.parse(fs.readFileSync('key.json'));
It is strongly recommended to create a new Google account for testing.
/* initialize sheet and authenticate */
const doc = new GoogleSpreadsheet(ID);
await doc.useServiceAccountAuth(creds);
await doc.loadInfo();
return doc;
}
**One small mistake could result in a block or ban from Google services.**
:::
:::caution pass
Google Sheets deprecates APIs quickly and there is no guarantee that the
referenced APIs will be available in the future.
:::
## Integration Details
This demo uses the following NodeJS modules:
- `google-auth-library`[^1] to authenticate with Google APIs
- `node-google-spreadsheet`[^2] to interact with Google Sheets v4 API
:::info Initial Setup
There are a number of steps to enable the Google Sheets API and Google Drive API
for an account. The [Complete Example](#complete-example) covers the process.
:::
### Authentication
It is strongly recommended to use a service account for Google API operations.
The ["Service Account Setup" section](#service-account-setup) covers how to
create a service account and generate a JSON key file.
```js title="Authenticate using a JSON key file"
import { JWT } from 'google-auth-library'
import { GoogleSpreadsheet } from 'google-spreadsheet';
// adjust the path to the actual key file.
// highlight-next-line
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
const jwt = new JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
```
</details>
### Connecting to Documents
To connect to existing documents, the document ID must be specified. This ID can
be found from the edit URL.
The edit URL starts with `https://docs.google.com/spreadsheets/d/` and includes
`/edit`. The ID is the string of characters between the slashes. For example:
```
https://docs.google.com/spreadsheets/d/a_long_string_of_characters/edit#gid=0
---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^--- ID
```
The `GoogleSpreadsheet` constructor accepts a document ID and auth object:
```js title="Connect to a document"
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
await doc.loadInfo();
```
### Creating New Documents
The `createNewSpreadsheetDocument` makes a request to create a new document. It
is strongly recommended to create a blank sheet.
```js title="Create a new document with blank sheet"
const doc = await GoogleSpreadsheet.createNewSpreadsheetDocument(jwt, { title: 'Document Title' });
const newSheet = await doc.addSheet({ title: 'Sheet Title' });
```
### Array of Arrays
["Arrays of Arrays"](/docs/api/utilities/array#array-of-arrays) are the main
data format for interchange with Google Sheets. The outer array object includes
row arrays, and each row array includes data.
SheetJS provides methods for working with Arrays of Arrays:
- `aoa_to_sheet`[^3] creates SheetJS worksheet objects from arrays of arrays
- `sheet_to_json`[^4] can generate arrays of arrays from SheetJS worksheets
## Export Document Data
The goal is to create an XLSB export from a Google Sheet. Google Sheets does
not natively support the XLSB format. SheetJS fills the gap.
<details><summary><b>How to run locally</b> (click to show)</summary>
0) Follow the [Initial Configuration](#initial-configuration).
1) Create a new Google Sheet and share with the generated service account. It
should be granted the "Editor" role
2) Install the dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet@3.3.0`}
</CodeBlock>
2) Save the following snippet to `common.js`:
```js title=common.js
const fs = require("fs");
const { GoogleSpreadsheet } = require('google-spreadsheet');
module.exports = async(ID) => {
/* get credentials */
const creds = JSON.parse(fs.readFileSync('key.json'));
/* initialize sheet and authenticate */
const doc = new GoogleSpreadsheet(ID);
await doc.useServiceAccountAuth(creds);
await doc.loadInfo();
return doc;
}
```
3) Save the following snippet to `pull.js`:
```js title=pull.js
const XLSX = require("xlsx");
/* create a blank workbook */
const wb = XLSX.utils.book_new();
const init = require("./common");
const ID = "<google sheet ID>";
(async() => {
const doc = await init(ID);
for(let i = 0; i < doc.sheetsByIndex.length; ++i) {
const sheet = doc.sheetsByIndex[i];
const name = sheet.title;
/* get the header and data rows */
await sheet.loadHeaderRow();
const header = sheet.headerValues;
const rows = await sheet.getRows();
const aoa = [header].concat(rows.map(r => r._rawData));
/* generate a SheetJS Worksheet */
const ws = XLSX.utils.aoa_to_sheet(aoa);
/* add to workbook */
XLSX.utils.book_append_sheet(wb, ws, name);
}
/* write to SheetJS.xlsb */
XLSX.writeFile(wb, "SheetJS.xlsb");
})();
```
4) Replace `<google sheet ID>` with the ID of the actual document.
5) Run `node pull.js` once. It will create `SheetJS.xlsb`.
6) Open `SheetJS.xlsb` and confirm the contents are the same as Google Sheets.
7) Change some cells in the Google Sheets Document.
8) Run `node pull.js` again and reopen `SheetJS.xlsb` to confirm value changes.
</details>
### Convert a Single Sheet
The idea is to extract the raw data from the Google Sheet headers and combine
@ -196,115 +183,8 @@ async function doc_to_wb(doc) {
## Update Document Data
The goal is to refresh a Google Sheet based on a local file.
<details><summary><b>How to run locally</b> (click to show)</summary>
0) Follow the [Initial Configuration](#initial-configuration).
1) Create a new Google Sheet and share with the generated service account. It
should be granted the "Editor" role
2) Install the dependencies:
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet@3.3.0`}
</CodeBlock>
2) Save the following snippet to `common.js`:
```js title=common.js
const fs = require("fs");
const { GoogleSpreadsheet } = require('google-spreadsheet');
module.exports = async(ID) => {
/* get credentials */
const creds = JSON.parse(fs.readFileSync('key.json'));
/* initialize sheet and authenticate */
const doc = new GoogleSpreadsheet(ID);
await doc.useServiceAccountAuth(creds);
await doc.loadInfo();
return doc;
}
```
3) Save the following snippet to `push.js`:
```js title=push.js
const XLSX = require("xlsx");
const fs = require("fs");
/* create dummy worksheet if `sheetjs.xlsx` does not exist */
if(!fs.existsSync("sheetjs.xlsx")) {
const wb = XLSX.utils.book_new();
const ws1 = XLSX.utils.aoa_to_sheet([["a","b","c"],[1,2,3]]); XLSX.utils.book_append_sheet(wb, ws1, "Sheet1");
const ws2 = XLSX.utils.aoa_to_sheet([["a","b","c"],[4,5,6]]); XLSX.utils.book_append_sheet(wb, ws2, "Sheet2");
XLSX.writeFile(wb, "sheetjs.xlsx");
}
/* read and parse sheetjs.xlsx */
const wb = XLSX.readFile("sheetjs.xlsx");
const init = require("./common");
const ID = "<google sheet ID>";
(async() => {
const doc = await init(ID);
/* clear workbook */
{
/* delete all sheets after the first sheet */
const old_sheets = doc.sheetsByIndex;
for(let i = 1; i < old_sheets.length; ++i) {
await old_sheets[i].delete();
}
/* clear first worksheet */
old_sheets[0].clear();
}
/* write worksheets */
{
const name = wb.SheetNames[0];
const ws = wb.Sheets[name];
/* first worksheet already exists */
const sheet = doc.sheetsByIndex[0];
/* update worksheet name */
await sheet.updateProperties({title: name});
/* generate array of arrays from the first worksheet */
const aoa = XLSX.utils.sheet_to_json(ws, {header: 1});
/* set document header row to first row of the AOA */
await sheet.setHeaderRow(aoa[0])
/* add the remaining rows */
await sheet.addRows(aoa.slice(1));
/* the other worksheets must be created manually */
for(let i = 1; i < wb.SheetNames.length; ++i) {
const name = wb.SheetNames[i];
const ws = wb.Sheets[name];
const sheet = await doc.addSheet({title: name});
const aoa = XLSX.utils.sheet_to_json(ws, {header: 1});
await sheet.setHeaderRow(aoa[0])
await sheet.addRows(aoa.slice(1));
}
}
})();
```
4) Replace `<google sheet ID>` with the ID of the actual document.
5) Run `node push.js` once. It will create `sheetjs.xlsx` and update the sheet.
6) Edit `sheetjs.xlsx` with some new data
7) Run `node push.js` again and watch the Google Sheet update!
</details>
The goal is to import data from a NUMBERS file to Google Sheets. Google Sheets
does not natively support the NUMBERS format. SheetJS fills the gap.
### Clear the Document
@ -428,75 +308,476 @@ async function sheet_load_from_ws(sheet, ws) {
## Raw File Exports
`node-google-spreadsheet` can download the XLSX or ODS export of the document.
The functions return NodeJS `Buffer` data that can be parsed using SheetJS.
In the web interface, Google Sheets can export documents to `XLSX` or `ODS`. The
NodeJS library includes similar methods to perform the download[^5]:
This example prints the worksheet names and CSV exports of each sheet.
| Format | Google Sheets Description | Method |
|:-------|:--------------------------|:-----------------|
| XLSX | Microsoft Excel (.xlsx) | `downloadAsXLSX` |
| ODS | OpenDocument (.ods) | `downloadAsODS` |
<Tabs>
<TabItem value="xlsx" label="XLSX">
The functions resolve to `Buffer` data. The `Buffer` objects can be parsed using
the SheetJS `read`[^6] method:
```js
const XLSX = require("xlsx");
/* download XLSX */
const ab = await doc.downloadAsXLSX();
(async() => {
/* Connect to Google Sheet */
const ID = "<google sheet id>";
const doc = await require("./common")(ID);
/* Get file export */
// highlight-next-line
const buf = await doc.downloadAsXLSX();
/* Parse with SheetJS */
const wb = XLSX.read(buf);
/* Loop over the worksheet names */
wb.SheetNames.forEach(name => {
/* Print the name to the console */
console.log(name);
/* Get the corresponding worksheet object */
const sheet = wb.Sheets[name];
/* Print a CSV export of the worksheet */
console.log(XLSX.utils.sheet_to_csv(sheet));
});
})();
/* parse */
const wb = XLSX.read(buf);
```
</TabItem>
At this point `wb` is a SheetJS workbook object[^7].
<TabItem value="ods" label="ODS">
## Complete Example
```js
const XLSX = require("xlsx");
:::note
(async() => {
/* Connect to Google Sheet */
const ID = "<google sheet id>";
const doc = await require("./common")(ID);
This demo was last tested on 2023 September 17 using `google-auth-library` for
authentication (`v8.9.0`) and `google-spreadsheet` for API access (`v4.1.0`).
/* Get file export */
// highlight-next-line
const buf = await doc.downloadAsODS();
:::
/* Parse with SheetJS */
const wb = XLSX.read(buf);
### Account Setup
/* Loop over the worksheet names */
wb.SheetNames.forEach(name => {
/* Print the name to the console */
console.log(name);
0) Create a new Google account or log into an existing account.
/* Get the corresponding worksheet object */
const sheet = wb.Sheets[name];
:::caution pass
/* Print a CSV export of the worksheet */
console.log(XLSX.utils.sheet_to_csv(sheet));
});
})();
A valid phone number (for SMS verification) may be required.
:::
1) Open <https://console.cloud.google.com> in a web browser. Review the Google
Cloud Platform Terms of Service.
:::warning pass
You must agree to the Google Cloud Platform Terms of Service to use the APIs.
:::
### Project Setup
2) Create a new Project.
If the account does not have an existing project, click "CREATE PROJECT"
If the account has an existing project, click the project selector (`:·` icon)
and click "NEW PROJECT" in the modal.
3) In the New Project screen, enter "SheetJS Test" in the Project name textbox
and select "No organization" in the Location box. Click "CREATE"
### API Setup
:::info pass
The goal of this section is to enable Google Sheets API and Google Drive API.
:::
4) Click the Project Selector (`:·` icon) and select "SheetJS Test"
5) In the left sidebar, click "Enabled APIs and services".
6) Near the top of the page, click "+ ENABLE APIS AND SERVICES".
7) In the search bar near the middle of the page, type "Sheets" and look for
"Google Sheets API". Click the card
8) In the Product Details screen, click the blue "ENABLE" button.
9) Click the left arrow (`<-`) next to API/Service details.
10) In the search bar near the middle of the page, type "Drive" and look for
"Google Drive API". Click the card.
11) In the Product Details screen, click the blue "ENABLE" button.
### Service Account Setup
:::info pass
The goal of this section is to create a service account and generate a JSON key.
:::
#### Create Service Account
12) Go to <https://console.cloud.google.com>.
13) Click the Project Selector (`:·` icon) and select "SheetJS Test".
14) Click "Dashboard".
15) In the left sidebar, hover over "APIs and Services" and select "Credentials"
16) Click "+ CREATE CREDENTIALS". In the dropdown, select "Service Account"
17) Enter "SheetJService" for Service account name. Click "CREATE AND CONTINUE"
:::note pass
The Service account ID is generated automatically.
:::
18) In Step 2 "Grant this service account access to project", click CONTINUE
19) In Step 3 click "DONE". You will be taken back to the credentials screen
#### Create JSON Key
20) Look for "SheetJService" in the "Service Accounts" table and click the email
address in the row
21) Click the email address of the account in the "Service Accounts" table.
22) Click "KEYS" in the horizontal bar near the top of the page.
23) Click "ADD KEY" and select "Create new key" in the dropdown.
24) In the popup, select the "JSON" radio button and click "CREATE". The page
will download a JSON file.
25) Click "CLOSE"
### Create Document
:::info pass
The goal of this section is to create a document from the service account and
share with the main account.
:::
26) Create a `SheetJSGS` folder and initialize:
```bash
mkdir SheetJSGS
cd SheetJSGS
npm init -y
```
</TabItem>
</Tabs>
27) Copy the JSON file from step 24 into the project folder.
28) Install dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet google-auth-library`}
</CodeBlock>
29) Save the following script to `init.mjs`:
```js title="init.mjs"
import { JWT } from 'google-auth-library'
import { GoogleSpreadsheet } from 'google-spreadsheet';
// highlight-next-line
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
const jwt = new JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
const doc = await GoogleSpreadsheet.createNewSpreadsheetDocument(jwt, { title: 'test from NodeJS' });
const newSheet = await doc.addSheet({ title: 'SheetJSTest' });
// highlight-next-line
await doc.share('YOUR_ADDRESS@gmail.com');
```
Edit the highlighted lines as follows:
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
JSON file in step 27. The `./` prefix is required!
- `'YOUR_ADDRESS@gmail.com'` should be replaced with the Google Account email
address from step 0.
30) Run the script:
```bash
node init.mjs
```
31) Sign into Google Sheets. A shared document "test from NodeJS" should be
displayed in the table. It will be owned by the service account.
32) Open the shared document from step 31.
33) Copy the URL and extract the document ID.
The URL of the document will look like
```
https://docs.google.com/spreadsheets/d/a_long_string_of_characters/edit#gid=0
---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^--- ID
```
The ID is a long string of letters and numbers and underscore characters (`_`)
just before the `/edit` part of the URL.
### Load Data from NUMBERS
:::info pass
The goal of this section is to update the new document with data from a sample
NUMBERS file.
:::
34) Download the [test file `pres.numbers`](https://sheetjs.com/pres.numbers):
```bash
curl -LO https://sheetjs.com/pres.numbers
```
35) Save the following script to `load.mjs`:
```js title="load.mjs"
import { JWT } from 'google-auth-library'
import { GoogleSpreadsheet } from 'google-spreadsheet';
import { set_fs, readFile, utils } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
// highlight-next-line
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
const jwt = new JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
// highlight-next-line
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
await doc.loadInfo();
const wb = readFile("pres.numbers");
/* clear workbook */
{
/* delete all sheets after the first sheet */
const old_sheets = doc.sheetsByIndex;
for(let i = 1; i < old_sheets.length; ++i) {
await old_sheets[i].delete();
}
/* clear first worksheet */
old_sheets[0].clear();
}
/* write worksheets */
{
const name = wb.SheetNames[0];
const ws = wb.Sheets[name];
/* first worksheet already exists */
const sheet = doc.sheetsByIndex[0];
/* update worksheet name */
await sheet.updateProperties({title: name});
/* generate array of arrays from the first worksheet */
const aoa = utils.sheet_to_json(ws, {header: 1});
/* set document header row to first row of the AOA */
await sheet.setHeaderRow(aoa[0])
/* add the remaining rows */
await sheet.addRows(aoa.slice(1));
/* the other worksheets must be created manually */
for(let i = 1; i < wb.SheetNames.length; ++i) {
const name = wb.SheetNames[i];
const ws = wb.Sheets[name];
const sheet = await doc.addSheet({title: name});
const aoa = utils.sheet_to_json(ws, {header: 1});
await sheet.setHeaderRow(aoa[0])
await sheet.addRows(aoa.slice(1));
}
}
```
Edit the highlighted lines as follows:
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
JSON file in step 27. The `./` prefix is required!
- `'DOCUMENT_ID'` should be replaced with the Document ID from step 33.
36) Run the script:
```bash
node load.mjs
```
37) Sign into Google Sheets and open the "test from NodeJS" shared document. It
should show a list of Presidents, matching the contents of the test file.
### Export Data to XLSB
:::info pass
The goal of this section is to export the raw data from Google Sheets to XLSB.
:::
38) Save the following script to `dump.mjs`:
```js title="dump.mjs"
import { JWT } from 'google-auth-library'
import { GoogleSpreadsheet } from 'google-spreadsheet';
import { set_fs, writeFile, utils } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
// highlight-next-line
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
const jwt = new JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
// highlight-next-line
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
await doc.loadInfo();
const wb = utils.book_new();
for(let i = 0; i < doc.sheetsByIndex.length; ++i) {
const sheet = doc.sheetsByIndex[i];
const name = sheet.title;
/* get the header and data rows */
await sheet.loadHeaderRow();
const header = sheet.headerValues;
const rows = await sheet.getRows();
const aoa = [header].concat(rows.map(r => r._rawData));
/* generate a SheetJS Worksheet */
const ws = utils.aoa_to_sheet(aoa);
/* add to workbook */
utils.book_append_sheet(wb, ws, name);
}
/* write to SheetJS.xlsb */
writeFile(wb, "SheetJS.xlsb");
```
Edit the highlighted lines as follows:
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
JSON file in step 27. The `./` prefix is required!
- `'DOCUMENT_ID'` should be replaced with the Document ID from step 33.
39) Run the script:
```bash
node load.mjs
```
The script should create a file `SheetJS.xlsb` in the project folder. This file
can be opened in Excel
40) Sign into Google Sheets and open the "test from NodeJS" shared document. It
should show a list of Presidents, matching the contents of the test file.
### Export Raw Files
:::info pass
The goal of this section is to parse the Google Sheets XLSX export and generate
CSV files for each worksheet.
:::
41) Sign into Google Sheets and open the "test from NodeJS" shared document.
42) Click the Plus (`+`) icon in the lower left corner to create a new worksheet.
43) In the new worksheet, set cell A1 to the formula `=SEQUENCE(3,5)`. This will
assign a grid of values
44) Save the following script to `raw.mjs`:
```js title="raw.mjs"
import { JWT } from 'google-auth-library'
import { GoogleSpreadsheet } from 'google-spreadsheet';
import { read, utils } from 'xlsx';
// highlight-next-line
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
const jwt = new JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
// highlight-next-line
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
await doc.loadInfo();
const buf = await doc.downloadAsXLSX();
/* Parse with SheetJS */
const wb = read(buf);
/* Loop over the worksheet names */
wb.SheetNames.forEach(name => {
/* Print the name to the console */
console.log(name);
/* Get the corresponding worksheet object */
const sheet = wb.Sheets[name];
/* Print a CSV export of the worksheet */
console.log(utils.sheet_to_csv(sheet));
});
```
Edit the highlighted lines as follows:
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
JSON file in step 27. The `./` prefix is required!
- `'DOCUMENT_ID'` should be replaced with the Document ID from step 33.
45) Run the script:
```bash
node raw.mjs
```
The script will display the sheet names and CSV rows from both worksheets.
[^1]: The package name is [`google-auth-library`](https://www.npmjs.com/package/google-auth-library)
[^2]: The project name is [`node-google-spreadsheet`](https://github.com/theoephraim/node-google-spreadsheet) but the module name is `google-spreadsheet`.
[^3]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^5]: See ["Exporting Data"](https://theoephraim.github.io/node-google-spreadsheet/#/guides/exports) in the `node-google-spreadsheet` documentation
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^7]: See ["Workbook Object"](/docs/csf/book) for a description of the workbook object or ["API Reference"](/docs/api) for various methods to work with workbook and sheet objects.

View File

@ -7,7 +7,7 @@ pagination_next: demos/bigdata/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
:::note
:::note pass
This demo focuses on Google Apps Script custom functions. For external data
processing, [the "Google Sheets" cloud data demo](/docs/demos/cloud/gsheet)
@ -150,7 +150,7 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
npx @google/clasp push
```
:::caution
:::caution pass
If the Google Apps Script API is not enabled, the command will display an object
with `code: 403` and an error message about the Apps Script API:

View File

@ -59,7 +59,7 @@ NodeJS push streams were introduced in 2012. The text streaming methods `to_csv`
and `to_html` are supported in NodeJS v0.10 and later while the object streaming
method `to_json` is supported in NodeJS v0.12 and later.
The first streaming write function, `to_csv`, was introduced in April 2017. It
The first streaming write function, `to_csv`, was introduced in early 2017. It
used and still uses the same NodeJS streaming API.
Years later, browser vendors are settling on a different stream API.

View File

@ -214,6 +214,7 @@ const config = {
{ from: '/docs/demos/clipboard', to: '/docs/demos/local/clipboard/' },
{ from: '/docs/demos/localfile', to: '/docs/demos/local/file/' },
{ from: '/docs/demos/data/indexeddb', to: '/docs/demos/local/indexeddb/' },
{ from: '/docs/demos/data/storageapi', to: '/docs/demos/local/storageapi/' },
/* desktop */
{ from: '/docs/demos/cli', to: '/docs/demos/desktop/cli/' },
{ from: '/docs/getting-started/demos/cli', to: '/docs/demos/desktop/cli/' },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 72 KiB