2022-10-19 10:05:59 +00:00
|
|
|
---
|
|
|
|
title: Web Workers
|
|
|
|
---
|
|
|
|
|
|
|
|
Parsing and writing large spreadsheets takes time. During the process, if the
|
|
|
|
SheetJS library is running in the web browser, the website may freeze.
|
|
|
|
|
|
|
|
Workers provide a way to off-load the hard work so that the website does not
|
|
|
|
freeze during processing.
|
|
|
|
|
|
|
|
:::note Browser Compatibility
|
|
|
|
|
|
|
|
IE10+ and modern browsers support basic Web Workers. Some APIs like `fetch` were
|
|
|
|
added later. Feature testing is highly recommended.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
## Installation
|
|
|
|
|
|
|
|
In all cases, `importScripts` can load the [Standalone scripts](../getting-started/installation/standalone)
|
|
|
|
|
|
|
|
```js
|
|
|
|
importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
|
|
|
|
```
|
|
|
|
|
|
|
|
For production use, it is highly encouraged to download and host the script.
|
|
|
|
|
2022-10-21 00:10:10 +00:00
|
|
|
<details><summary><b>ECMAScript Module Support</b> (click to show)</summary>
|
|
|
|
|
|
|
|
:::note Browser Compatibility
|
|
|
|
|
|
|
|
ESM is supported in Web Workers in the Chromium family of browsers (including
|
|
|
|
Chrome and Edge) as well as in Webkit-based browsers (including Safari).
|
|
|
|
|
|
|
|
For support in legacy browsers like Firefox, `importScripts` should be used.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs";
|
|
|
|
```
|
|
|
|
|
|
|
|
When using modules, the script must be served with the correct MIME type and the
|
|
|
|
Worker constructor must set the `type` option:
|
|
|
|
|
|
|
|
```js
|
|
|
|
const worker_code = `\
|
|
|
|
/* load standalone script from CDN */
|
|
|
|
import * as XLSX from "https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs";
|
|
|
|
// ... do something with XLSX here ...
|
|
|
|
`;
|
|
|
|
const worker = new Worker(
|
|
|
|
URL.createObjectURL(
|
|
|
|
new Blob(
|
|
|
|
[ worker_code ],
|
|
|
|
// highlight-next-line
|
|
|
|
{ type: "text/javascript" } // second argument to the Blob constructor
|
|
|
|
)
|
|
|
|
),
|
|
|
|
// highlight-next-line
|
|
|
|
{type: "module"} // second argument to Worker constructor
|
|
|
|
);
|
|
|
|
```
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
2022-10-19 10:05:59 +00:00
|
|
|
## Downloading a Remote File
|
|
|
|
|
2022-10-21 00:10:10 +00:00
|
|
|
:::note fetch in Web Workers
|
2022-10-19 10:05:59 +00:00
|
|
|
|
|
|
|
`fetch` was enabled in Web Workers in Chrome 42 and Safari 10.3
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
Typically the Web Worker performs the `fetch` operation, processes the workbook,
|
|
|
|
and sends a final result to the main browser context for processing.
|
|
|
|
|
|
|
|
In the following example, the script:
|
|
|
|
|
|
|
|
- downloads <https://sheetjs.com/pres.numbers> in a Web Worker
|
|
|
|
- loads the SheetJS library and parses the file in the Worker
|
|
|
|
- generates an HTML string of the first table in the Worker
|
|
|
|
- sends the string to the main browser context
|
|
|
|
- adds the HTML to the page in the main browser context
|
|
|
|
|
|
|
|
```jsx live
|
|
|
|
function SheetJSFetchDLWorker() {
|
2022-10-21 00:10:10 +00:00
|
|
|
const [__html, setHTML] = React.useState("");
|
2022-10-19 10:05:59 +00:00
|
|
|
|
|
|
|
return ( <>
|
|
|
|
<button onClick={() => {
|
|
|
|
/* this mantra embeds the worker source in the function */
|
|
|
|
const worker = new Worker(URL.createObjectURL(new Blob([`\
|
|
|
|
/* load standalone script from CDN */
|
|
|
|
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) => {
|
|
|
|
try {
|
|
|
|
/* Fetch file */
|
|
|
|
const res = await fetch("https://sheetjs.com/pres.numbers");
|
|
|
|
const ab = await res.arrayBuffer();
|
|
|
|
|
|
|
|
/* Parse file */
|
|
|
|
const wb = XLSX.read(ab);
|
|
|
|
const ws = wb.Sheets[wb.SheetNames[0]];
|
|
|
|
|
|
|
|
/* Generate HTML */
|
|
|
|
const html = XLSX.utils.sheet_to_html(ws);
|
|
|
|
|
|
|
|
/* Reply with result */
|
2022-10-21 00:10:10 +00:00
|
|
|
postMessage({ html });
|
2022-10-19 10:05:59 +00:00
|
|
|
} catch(e) {
|
|
|
|
/* Pass the error message back */
|
|
|
|
postMessage({html: String(e.message || e).bold() });
|
|
|
|
}
|
|
|
|
}, false);
|
|
|
|
`])));
|
|
|
|
/* when the worker sends back the HTML, add it to the DOM */
|
|
|
|
worker.onmessage = function(e) { setHTML(e.data.html); };
|
|
|
|
/* post a message to the worker */
|
|
|
|
worker.postMessage({});
|
|
|
|
}}><b>Click to Start</b></button>
|
2022-10-21 00:10:10 +00:00
|
|
|
<div dangerouslySetInnerHTML={{ __html }}/>
|
2022-10-19 10:05:59 +00:00
|
|
|
</> );
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## Creating a Local File
|
|
|
|
|
2022-10-21 00:10:10 +00:00
|
|
|
:::caution Writing files from Web Workers
|
2022-10-19 10:05:59 +00:00
|
|
|
|
|
|
|
`XLSX.writeFile` will not work in Web Workers! Raw file data can be passed from
|
|
|
|
the Web Worker to the main browser context for downloading.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
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
|
2022-10-19 21:12:12 +00:00
|
|
|
- generates an object URL in the Web Worker
|
|
|
|
- sends the object URL to the main browser context
|
2022-10-19 10:05:59 +00:00
|
|
|
- performs a download action in the main browser context
|
|
|
|
|
|
|
|
```jsx live
|
|
|
|
function SheetJSWriteFileWorker() {
|
2022-10-21 00:10:10 +00:00
|
|
|
const [__html, setHTML] = React.useState("");
|
2022-10-19 10:05:59 +00:00
|
|
|
|
|
|
|
return ( <>
|
|
|
|
<button onClick={() => { setHTML("");
|
|
|
|
/* this mantra embeds the worker source in the function */
|
|
|
|
const worker = new Worker(URL.createObjectURL(new Blob([`\
|
|
|
|
/* load standalone script from CDN */
|
|
|
|
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) => {
|
|
|
|
try {
|
|
|
|
/* Create a new Workbook (in this case, from a CSV string) */
|
|
|
|
const csv = \`\
|
|
|
|
SheetJS,in,Web,Workers
|
|
|
|
வணக்கம்,สวัสดี,你好,가지마
|
|
|
|
1,2,3,4\`;
|
|
|
|
const wb = XLSX.read(csv, { type: "string" });
|
|
|
|
|
2022-10-19 21:12:12 +00:00
|
|
|
/* Write XLSB data (Uint8Array) */
|
2022-10-19 10:05:59 +00:00
|
|
|
const u8 = XLSX.write(wb, { bookType: "xlsb", type: "buffer" });
|
|
|
|
|
2022-10-19 21:12:12 +00:00
|
|
|
/* Generate URL */
|
|
|
|
const url = URL.createObjectURL(new Blob([u8]));
|
|
|
|
|
2022-10-19 10:05:59 +00:00
|
|
|
/* Reply with result */
|
2022-10-19 21:12:12 +00:00
|
|
|
postMessage({ url });
|
2022-10-19 10:05:59 +00:00
|
|
|
} catch(e) {
|
|
|
|
/* Pass the error message back */
|
|
|
|
postMessage({error: String(e.message || e).bold() });
|
|
|
|
}
|
|
|
|
}, false);
|
|
|
|
`])));
|
|
|
|
/* when the worker sends back the data, create a download */
|
|
|
|
worker.onmessage = function(e) {
|
|
|
|
if(e.data.error) return setHTML(e.data.error);
|
|
|
|
|
|
|
|
/* this mantra is the standard HTML5 download attribute technique */
|
|
|
|
const a = document.createElement("a");
|
|
|
|
a.download = "SheetJSWriteFileWorker.xlsb";
|
2022-10-19 21:12:12 +00:00
|
|
|
a.href = e.data.url;
|
2022-10-19 10:05:59 +00:00
|
|
|
document.body.appendChild(a);
|
|
|
|
a.click();
|
|
|
|
document.body.removeChild(a);
|
|
|
|
};
|
|
|
|
/* post a message to the worker */
|
|
|
|
worker.postMessage({});
|
|
|
|
}}><b>Click to Start</b></button>
|
2022-10-21 00:10:10 +00:00
|
|
|
<div dangerouslySetInnerHTML={{ __html }}/>
|
2022-10-19 10:05:59 +00:00
|
|
|
</> );
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## User-Submitted File
|
|
|
|
|
2022-10-21 00:10:10 +00:00
|
|
|
:::note FileReaderSync
|
2022-10-19 10:05:59 +00:00
|
|
|
|
|
|
|
Typically `FileReader` is used in the main browser context. In Web Workers, the
|
|
|
|
synchronous version `FileReaderSync` is more efficient.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
2022-10-21 00:10:10 +00:00
|
|
|
In the following example, when a file is dropped over the DIV or when the INPUT
|
|
|
|
element is used to select a file, the script:
|
2022-10-19 10:05:59 +00:00
|
|
|
|
|
|
|
- sends the `File` object to the Web Worker
|
|
|
|
- loads the SheetJS library and parses the file in the Worker
|
|
|
|
- generates an HTML string of the first table in the Worker
|
|
|
|
- sends the string to the main browser context
|
|
|
|
- adds the HTML to the page in the main browser context
|
|
|
|
|
|
|
|
```jsx live
|
|
|
|
function SheetJSDragDropWorker() {
|
2022-10-21 00:10:10 +00:00
|
|
|
const [__html, setHTML] = React.useState("");
|
2022-10-19 21:12:12 +00:00
|
|
|
/* suppress default behavior for drag and drop */
|
2022-10-19 10:05:59 +00:00
|
|
|
function suppress(e) { e.stopPropagation(); e.preventDefault(); }
|
|
|
|
|
2022-10-21 00:10:10 +00:00
|
|
|
/* this worker is shared between drag-drop and file input element */
|
|
|
|
const worker = new Worker(URL.createObjectURL(new Blob([`\
|
2022-10-19 10:05:59 +00:00
|
|
|
/* load standalone script from CDN */
|
|
|
|
importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
|
|
|
|
|
|
|
|
/* this callback will run once the main context sends a message */
|
2022-10-19 21:12:12 +00:00
|
|
|
self.addEventListener('message', (e) => {
|
2022-10-19 10:05:59 +00:00
|
|
|
try {
|
|
|
|
/* Read file data */
|
|
|
|
const ab = new FileReaderSync().readAsArrayBuffer(e.data.file);
|
|
|
|
|
|
|
|
/* Parse file */
|
|
|
|
const wb = XLSX.read(ab);
|
|
|
|
const ws = wb.Sheets[wb.SheetNames[0]];
|
|
|
|
|
|
|
|
/* Generate HTML */
|
|
|
|
const html = XLSX.utils.sheet_to_html(ws);
|
|
|
|
|
|
|
|
/* Reply with result */
|
2022-10-21 00:10:10 +00:00
|
|
|
postMessage({ html });
|
2022-10-19 10:05:59 +00:00
|
|
|
} catch(e) {
|
|
|
|
/* Pass the error message back */
|
|
|
|
postMessage({html: String(e.message || e).bold() });
|
|
|
|
}
|
|
|
|
}, false);
|
2022-10-21 00:10:10 +00:00
|
|
|
`])));
|
|
|
|
/* when the worker sends back the HTML, add it to the DOM */
|
|
|
|
worker.onmessage = function(e) { setHTML(e.data.html); };
|
|
|
|
return ( <>
|
|
|
|
<div onDragOver={suppress} onDragEnter={suppress} onDrop={(e) => {
|
|
|
|
suppress(e);
|
2022-10-19 10:05:59 +00:00
|
|
|
/* post a message with the first File to the worker */
|
|
|
|
worker.postMessage({ file: e.dataTransfer.files[0] });
|
2022-10-21 00:10:10 +00:00
|
|
|
}}>Drag a file to this DIV to process! (or use the file input)</div>
|
|
|
|
<input type="file" onChange={(e) => {
|
|
|
|
suppress(e);
|
|
|
|
/* post a message with the first File to the worker */
|
|
|
|
worker.postMessage({ file: e.target.files[0] });
|
|
|
|
}}/>
|
|
|
|
<div dangerouslySetInnerHTML={{ __html }}/>
|
2022-10-19 10:05:59 +00:00
|
|
|
</> );
|
|
|
|
}
|
|
|
|
```
|