This commit is contained in:
SheetJS 2022-10-19 17:12:12 -04:00
parent a6a981226f
commit 22720fd7a6
3 changed files with 142 additions and 77 deletions

@ -202,7 +202,7 @@ many additional features including massive data streaming, sorting and styling.
### Tabulator
[Tabulator](http://tabulator.info/docs/5.3/download#xlsx) includes deep support
[Tabulator](https://tabulator.info/docs/5.4/download#xlsx) includes deep support
through a special Export button. It handles the SheetJS operations internally.

@ -100,7 +100,8 @@ In the following example, the script:
- generates a workbook object in the Web Worker
- generates a XLSB file using `XLSX.write` in the Web Worker
- sends the file (`Uint8Array`) to the main browser context
- generates an object URL in the Web Worker
- sends the object URL to the main browser context
- performs a download action in the main browser context
```jsx live
@ -124,11 +125,14 @@ SheetJS,in,Web,Workers
1,2,3,4\`;
const wb = XLSX.read(csv, { type: "string" });
/* Write XLSB data */
/* Write XLSB data (Uint8Array) */
const u8 = XLSX.write(wb, { bookType: "xlsb", type: "buffer" });
/* Generate URL */
const url = URL.createObjectURL(new Blob([u8]));
/* Reply with result */
postMessage({data: u8});
postMessage({ url });
} catch(e) {
/* Pass the error message back */
postMessage({error: String(e.message || e).bold() });
@ -142,7 +146,7 @@ SheetJS,in,Web,Workers
/* this mantra is the standard HTML5 download attribute technique */
const a = document.createElement("a");
a.download = "SheetJSWriteFileWorker.xlsb";
a.href = URL.createObjectURL(new Blob([e.data.data]));
a.href = e.data.url;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
@ -176,10 +180,10 @@ In the following example, the script:
```jsx live
function SheetJSDragDropWorker() {
const [html, setHTML] = React.useState("");
/* suppress default behavior for dragover and drop */
/* suppress default behavior for drag and drop */
function suppress(e) { e.stopPropagation(); e.preventDefault(); }
return ( <>
<div onDragOver={suppress} onDrop={(e) => {
<div onDragOver={suppress} onDragEnter={suppress} onDrop={(e) => {
suppress(e);
/* this mantra embeds the worker source in the function */
@ -188,7 +192,7 @@ function SheetJSDragDropWorker() {
importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
/* this callback will run once the main context sends a message */
self.addEventListener('message', async(e) => {
self.addEventListener('message', (e) => {
try {
/* Read file data */
const ab = new FileReaderSync().readAsArrayBuffer(e.data.file);

@ -2,10 +2,11 @@
title: Local File Access
---
Reading and writing files require native support. `readFile` and `writeFile`
include support for some approaches but do not support every API. When an API
is not supported by `readFile` or `writeFile`, the underlying `read` and
`write` methods can be used.
Reading and writing files require native platform support. `XLSX.readFile` and
`XLSX.writeFile` include support for some APIs.
For other APIs, user code can pass data to `XLSX.read` or use data generated by
`XLSX.write` directly. Both methods work with a number of common storage types.
This demo looks at various web APIs. More specific approaches for deployments
like mobile apps are covered in their respective demos.
@ -35,20 +36,63 @@ Modern browser APIs typically use typed arrays or `Blob` or `File` structures.
_Reading Binary Data_
Given a `Blob` or `File`, the underlying data cannot be read synchronously!
`XLSX.read` supports `Uint8Array` and `ArrayBuffer` data. For `Blob` or `File`
objects, the underlying data must be pulled into an `ArrayBuffer`.
The callback-based approach uses a `FileReader`:
```js
const reader = new FileReader();
reader.onload = function(e) {
/* e.target.result is an ArrayBuffer */
const wb = XLSX.read(e.target.result);
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
// usage: file_to_wb(file, function(wb) { /* wb is a workbook object */ });
function file_to_wb(file, callback) {
var reader = new FileReader();
reader.onload = function(e) {
/* e.target.result is an ArrayBuffer */
callback(XLSX.read(e.target.result));
};
reader.readAsArrayBuffer(file);
}
reader.readAsArrayBuffer(file);
```
<details><summary><b>FileReaderSync in Web Workers</b> (click to show)</summary>
`FileReaderSync` is only available in Web Workers:
```js
// assuming main thread called worker.postMessage({ file: file_object })
self.addEventListener('message', (e) => {
/* get file object from message */
var file = e.data.file;
/* Read file data */
const ab = new FileReaderSync().readAsArrayBuffer(file);
/* Parse file */
const wb = XLSX.read(ab);
/* DO SOMETHING WITH wb HERE */
});
```
["User-Submitted File" example](./worker#user-submitted-file) has a live demo.
</details>
<details><summary><b>IE10 Binary Strings</b> (click to show)</summary>
In IE10, binary strings are more performant than `ArrayBuffer`. `XLSX.read`
supports binary strings with `type: "binary"`:
```js
// usage: file_bs_to_wb(file, function(wb) { /* wb is a workbook object */ });
function file_bs_to_wb(file, callback) {
var reader = new FileReader();
reader.onload = function(e) {
/* e.target.result is a binary string */
callback(XLSX.read(e.target.result, { type: "binary" }));
};
reader.readAsBinaryString(file);
}
```
</details>
The Promise-based approach uses `Blob#arrayBuffer`:
```js
@ -60,8 +104,9 @@ async function blob_to_wb(blob) {
_Writing Binary Data_
`XLSX.write` can generate `Uint8Array` results by passing `type: "buffer"`. A
`Blob` can be created by using the constructor:
`XLSX.write` can generate `Uint8Array` results by passing `type: "buffer"`.
A `Blob` can be created by using the constructor:
```js
const u8 = XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });
@ -78,6 +123,17 @@ _Writing Files_
XLSX.writeFile(wb, "SheetJS.xlsx");
```
:::caution Web Workers
`XLSX.writeFile` requires DOM access and will not work in a Web Worker!
The workaround is to generate the file data from the Worker (using `XLSX.write`)
and send the data back to the main context for the actual download action.
["Creating a Local File" includes a live demo](./worker#creating-a-local-file).
:::
## File API
_Reading Files_
@ -93,7 +149,7 @@ async function handleFileAsync(e) {
/* data is an ArrayBuffer */
const workbook = XLSX.read(data);
/* do something with the workbook here */
console.log(XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]))
console.log(XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]));
}
input_dom_element.addEventListener("change", handleFileAsync, false);
```
@ -105,8 +161,12 @@ _Reading Files_
The `dataTransfer` property of the `drop` event holds a list of files:
```js
/* suppress default behavior for drag and drop events */
function suppress(e) { e.stopPropagation(); e.preventDefault(); }
/* handle data from drop event */
async function handleDropAsync(e) {
e.stopPropagation(); e.preventDefault();
suppress(e);
/* get first file */
const f = e.dataTransfer.files[0];
/* get raw data */
@ -114,9 +174,12 @@ async function handleDropAsync(e) {
/* data is an ArrayBuffer */
const wb = XLSX.read(data);
/* do something with the workbook here */
console.log(XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]))
console.log(XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]));
}
drop_dom_element.addEventListener("drop", handleDropAsync, false);
drop_dom_element.addEventListener("dragover", suppress, false);
drop_dom_element.addEventListener("dragenter", suppress, false);
```
## File System Access API
@ -128,6 +191,45 @@ the feature in version 86. Safari did not support File System Access API.
:::
<details><summary><b>Live Example</b> (click to show) </summary>
This live example reads a file then tries to save as XLSX.
```jsx live
function SheetJSRoundTripFileSystemAPI() { return ( <button onClick={async () => {
/* Show picker and get data */
const [rFile] = await window.showOpenFilePicker({
types: [{
description: 'Spreadsheets',
accept: { 'application/vnd.ms-excel': ['.xlsx', '.xls', '.xlsb', /*...*/] }
}],
excludeAcceptAllOption: true,
multiple: false
});
const ab = await (await rFile.getFile()).arrayBuffer();
/* parse */
const wb = XLSX.read(ab);
/* Show picker and get handle to file */
const wFile = await window.showSaveFilePicker({
suggestedName: "SheetJSRT.xlsx",
types: [ { description: 'XLSX', accept: { 'application/vnd.ms-excel': ['.xlsx'] } } ]
});
const wstream = await wFile.createWritable();
/* write */
const buf = XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
wstream.write(buf);
/* close stream to commit file */
wstream.close();
}}>Click to read then save as XLSX</button> ) }
```
</details>
_Reading Files_
`window.showOpenFilePicker` shows a file picker and resolves to an array of
@ -182,47 +284,6 @@ wstream.write(XLSX.write(wb, { bookType: ext, type: "buffer" }))
wstream.close();
```
### Demo
<details><summary><b>Live Example</b> (click to show) </summary>
This live example reads a file then tries to save as XLSX.
```jsx live
function SheetJSRoundTripFileSystemAPI() { return ( <button onClick={async () => {
/* Show picker and get data */
const [rFile] = await window.showOpenFilePicker({
types: [{
description: 'Spreadsheets',
accept: { 'application/vnd.ms-excel': ['.xlsx', '.xls', '.xlsb', /*...*/] }
}],
excludeAcceptAllOption: true,
multiple: false
});
const ab = await (await rFile.getFile()).arrayBuffer();
/* parse */
const wb = XLSX.read(ab);
/* Show picker and get handle to file */
const wFile = await window.showSaveFilePicker({
suggestedName: "SheetJSRT.xlsx",
types: [ { description: 'XLSX', accept: { 'application/vnd.ms-excel': ['.xlsx'] } } ]
});
const wstream = await wFile.createWritable();
/* write */
const buf = XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
wstream.write(buf);
/* close stream to commit file */
wstream.close();
}}>Click to read then save as XLSX</button> ) }
```
</details>
## File and Directory Entries API
:::caution Deprecated
@ -263,7 +324,8 @@ Internet Explorer offered proprietary APIs that were not adopted by Chromium.
_Writing Files_
IE10 and IE11 support `navigator.msSaveBlob`. `writeFile` will use the method.
IE10 and IE11 support `navigator.msSaveBlob`. `XLSX.writeFile` will use this
method if it is available.
#### VBScript
@ -304,8 +366,8 @@ import { readFile, writeFile, set_fs } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
var wb = XLSX.readFile("sheetjs.numbers");
XLSX.writeFile(wb, "sheetjs.xlsx");
var wb = readFile("sheetjs.numbers");
writeFile(wb, "sheetjs.xlsx");
```
### ExtendScript
@ -322,8 +384,7 @@ XLSX.writeFile(wb, "sheetjs.csv");
### Deno
`Deno.readFileSync` and `Deno.writeFileSync` are supported by `readFile` and
`writeFile` out of the box:
`readFile` uses `Deno.readFileSync` and `writeFile` uses `Deno.writeFileSync`:
```js
// @deno-types="https://cdn.sheetjs.com/xlsx-latest/package/types/index.d.ts"
@ -333,11 +394,11 @@ const wb = XLSX.readFile("sheetjs.numbers");
XLSX.writeFile(wb, "sheetjs.xlsx");
```
:::note
:::caution Deno entitlements
Any script using `XLSX.readFile` requires the `--allow-read` permission.
Any Deno script using `XLSX.readFile` requires the `--allow-read` entitlement.
Any script using `XLSX.writeFile` requires the `--allow-write` permission.
Any Deno script using `XLSX.writeFile` requires the `--allow-write` entitlement.
:::
@ -346,12 +407,12 @@ Any script using `XLSX.writeFile` requires the `--allow-write` permission.
Bun requires the `fs` module:
```js
import * as XLSX from 'xlsx';
import { readFile, writeFile, set_fs } from 'xlsx';
import * as fs from 'fs';
XLSX.set_fs(fs);
set_fs(fs);
var wb = XLSX.readFile("sheetjs.numbers");
XLSX.writeFile(wb, "sheetjs.xlsx");
var wb = readFile("sheetjs.numbers");
writeFile(wb, "sheetjs.xlsx");
```
### Apps