forked from sheetjs/docs.sheetjs.com
		
	gsheet-api
This commit is contained in:
		
							parent
							
								
									4438bd86cd
								
							
						
					
					
						commit
						8914e8f714
					
				| @ -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: | ||||
| 
 | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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. | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| :::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> | ||||
| @ -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> | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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 | 
		Loading…
	
		Reference in New Issue
	
	Block a user