---
title: Local File Access
pagination_prev: demos/data/index
pagination_next: demos/cloud/index
sidebar_custom_props:
summary: Reading and writing files using various platform APIs
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
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.
:::note
Some snippets are also available in the "Common Use Cases" section:
- [Data Import](/docs/solutions/input)
- [Data Export](/docs/solutions/output)
:::
:::warning
Not all web APIs are supported in all browsers. For example, Firefox does not
support the "File System Access API".
Even when a browser technically supports a web API, it may be disabled in the
client browser. Some APIs do not give any feedback.
:::
## Binary Data
Modern browser APIs typically use typed arrays or `Blob` or `File` structures.
_Reading Binary Data_
`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
// 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);
}
```
FileReaderSync in Web Workers (click to show)
`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](/docs/demos/bigdata/worker#user-submitted-file)
includes a live demo.
IE10 Binary Strings (click to show)
`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);
}
```
The Promise-based approach uses `Blob#arrayBuffer`:
```js
// usage: const wb = await blob_to_wb(blob);
async function blob_to_wb(blob) {
return XLSX.read(await blob.arrayBuffer());
}
```
_Writing Binary Data_
`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" });
const blob = new Blob([u8], { type: "application/vnd.ms-excel" });
```
## HTML5 Download Attribute
_Writing Files_
`writeFile` will attempt a download in the browser using the attribute.
```js
XLSX.writeFile(wb, "SheetJS.xlsx");
```
Implementation Details (click to show)
Under the hood, it creates a special URL and clicks a link. The library method
includes a few workarounds for legacy browsers
`XLSX.writeFile(wb, "SheetJS.xlsx");` is roughly equivalent to:
```js
/* write data -- note that writeFile infers bookType from filename */
const u8 = XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
/* create Blob */
const blob = new Blob([u8]);
/* create object URL */
const url = URL.createObjectURL(new Blob([u8]));
/* create `A` DOM element */
const a = document.createElement("a");
/* set export file name */
a.download = "SheetJS.xlsx";
/* wire up the object URL to the DOM element */
a.href = url;
/* add to the page */
document.body.appendChild(a);
/* click the link */
a.click();
/* remove the element from the page */
document.body.removeChild(a);
```
:::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"](/docs/demos/bigdata/worker#creating-a-local-file)
includes a live demo.
:::
## File API
_Reading Files_
In the `change` event of ``, `target` hold a list of files:
```js
async function handleFileAsync(e) {
/* get first file */
const file = e.target.files[0];
/* get raw data */
const data = await file.arrayBuffer();
/* 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]]));
}
input_dom_element.addEventListener("change", handleFileAsync, false);
```
## HTML Drag and Drop API
_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) {
suppress(e);
/* get first file */
const f = e.dataTransfer.files[0];
/* get raw data */
const data = await f.arrayBuffer();
/* 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]]));
}
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
:::caution Limited Browser Support
At the time of writing, browser support was fairly limited. Chrome introduced
the feature in version 86. Safari did not support File System Access API.
:::
Live Example (click to show)
This live example reads a file then tries to save as XLSX.
```jsx live
function SheetJSRoundTripFileSystemAPI() { return ( ) }
```
_Reading Files_
`window.showOpenFilePicker` shows a file picker and resolves to an array of
file handles. When `multiple: false` is set, the array has one element.
The `getFile` method resolves to a `File` object whose data can be read with
the `arrayBuffer` method:
```js
/* Show picker and get data */
const [hFile] = await window.showOpenFilePicker({
types: [{
description: 'Spreadsheets',
accept: { 'application/vnd.ms-excel': ['.xlsx', '.xls', '.xlsb', /*...*/] }
}],
excludeAcceptAllOption: true,
multiple: false
});
const ab = await (await hFile.getFile()).arrayBuffer();
/* parse */
const wb = XLSX.read(ab);
/* do something with the workbook */
console.log(XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]));
```
_Writing Files_
`window.showSaveFilePicker` shows a file picker and resolves to a file handle.
The `createWritable` method resolves to a `FileSystemWritableFileStream`, which
readily accepts `Uint8Array` data from `XLSX.write`:
```js
/* Show picker and get handle to file */
const hFile = await window.showSaveFilePicker({
suggestedName: "SheetJS.xlsx",
types: [
{ description: 'Excel 2007+ (XLSX)', accept: { 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'] } },
{ description: 'Excel 97-2004 (XLS)', accept: { 'application/vnd.ms-excel': ['.xls'] } },
{ description: 'Excel 2007+ Binary (XLSB)', accept: { 'application/vnd.ms-excel.sheet.binary.macroEnabled.12': ['.xlsb'] } },
/* note that each MIME type must be unique! */
]
});
const wstream = await hFile.createWritable();
/* get extension */
const ext = hFile.name.slice(hFile.name.lastIndexOf(".")+1)
/* write */
wstream.write(XLSX.write(wb, { bookType: ext, type: "buffer" }))
/* close stream to commit file */
wstream.close();
```
## File and Directory Entries API
:::caution Deprecated
In the web browser, the File and Directory Entries API has been deprecated and
is not recommended for new applications.
`cordova-plugin-file` still uses the API patterns.
:::
_Writing Files_
```js
// Request File System Access
window.requestFileSystem(window.PERSISTENT, 0, (fs) => {
// Request a handle to "SheetJS.xlsx", making a new file if necessary
fs.root.getFile("SheetJS.xlsx", {create: true}, entry => {
// Request a FileWriter for writing data
entry.createWriter(writer => {
// The FileWriter API needs an actual Blob
const u8 = XLSX.write(wb, { type: "buffer", bookType: "xlsx" });
const data = new Blob([u8], { type: "application/vnd.ms-excel" });
// `onwriteend` is called on success, `onerror` called on error
writer.onwriteend = () => {}; writer.onerror = () => {};
// write the data
writer.write(data);
});
});
});
```
## Internet Explorer
Internet Explorer offered proprietary APIs that were not adopted by Chromium.
#### Blob API
_Writing Files_
IE10 and IE11 support `navigator.msSaveBlob`. `XLSX.writeFile` will use this
method if it is available.
#### VBScript
_Reading and Writing Files_
Internet Explorer 6-9 with VBScript support `Scripting.FileSystemObject`. This
is not supported in modern browsers.
This approach is implemented in the library `readFile` and `writeFile` methods.
It requires the shim script to be loaded before the main library script:
```html
```
## Other Platforms
### NodeJS
`fs.readFileSync` and `fs.writeFileSync` allow for reading and writing files.
When using `require`, these are supported in `readFile` and `writeFile`:
```js
var XLSX = require("xlsx");
var wb = XLSX.readFile("sheetjs.numbers");
XLSX.writeFile(wb, "sheetjs.xls");
```
[Installation](/docs/getting-started/installation/nodejs) has a special note for
use with NodeJS ECMAScript Modules:
```js
import { readFile, writeFile, set_fs } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
var wb = readFile("sheetjs.numbers");
writeFile(wb, "sheetjs.xlsx");
```
Implementation Details (click to show)
**`XLSX.readFile(filepath)`** is equivalent to:
_CommonJS_
```js
var fs = require("fs");
var buf = fs.readFileSync(filepath);
var wb = XLSX.read(buf);
```
_ECMAScript Modules_
```js
import { read } from "xlsx";
import { readFileSync } from "fs";
var buf = readFileSync(filepath);
var wb = read(buf);
```
**`XLSX.writeFile(wb, filepath)`** is equivalent to:
_CommonJS_
```js
var fs = require("fs"), path = require("path");
var buf = XLSX.write(wb, { bookType: path.extname(filepath).slice(1), type: "buffer" });
fs.writeFileSync(filepath, buf);
```
_ECMAScript Modules_
```js
import { write } from "xlsx";
import { writeFileSync } from "fs";
import { extname } from "path";
var buf = write(wb, { bookType: extname(filepath).slice(1), type: "buffer" });
writeFileSync(filepath, buf);
```
### ExtendScript
In Photoshop and other Adobe apps, `readFile` and `writeFile` use the `File`
object under the hood:
```js
#include "xlsx.extendscript.js"
var wb = XLSX.readFile("sheetjs.xlsx");
XLSX.writeFile(wb, "sheetjs.csv");
```
The [ExtendScript demo](/docs/demos/extensions/extendscript) covers the "Common
Extensibility Platform" (CEP) and "Unified Extensibility Platform" (UXP) details.
### Chrome Extensions
In Manifest v2 Chrome extensions, `writeFile` calls `chrome.downloads.download`.
This approach uses `URL.createObjectURL`, an API that is not supported in a
Manifest v3 Background Service Worker. For small exports, raw Base64 URLs can be
generated and downloaded.
The [Chromium demo](/docs/demos/extensions/chromium) covers the details.
### Deno
`readFile` uses `Deno.readFileSync` and `writeFile` uses `Deno.writeFileSync`:
{`\
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
\n\
const wb: XLSX.WorkBook = XLSX.readFile("sheetjs.numbers");
XLSX.writeFile(wb, "sheetjs.xlsx");`}
:::caution Deno entitlements
Any Deno script using `XLSX.readFile` requires the `--allow-read` entitlement.
Any Deno script using `XLSX.writeFile` requires the `--allow-write` entitlement.
:::
Implementation Details (click to show)
**`XLSX.readFile(filepath)`** is equivalent to:
_ECMAScript Modules_
{`\
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
\n\
const u8: Uint8Array = Deno.readFileSync(filepath);
const wb: XLSX.WorkBook = XLSX.read(u8);`}
**`XLSX.writeFile(wb, filepath)`** is equivalent to:
_ECMAScript Modules_
{`\
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
\n\
const u8 = XLSX.write(wb, { bookType: filepath.slice(filepath.lastIndexOf(".")+1), type: "buffer" });
Deno.writeFileSync(filepath, u8);`}
### Bun
Bun requires the `fs` module:
```js
import { readFile, writeFile, set_fs } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
var wb = readFile("sheetjs.numbers");
writeFile(wb, "sheetjs.xlsx");
```
The implementation is identical to [NodeJS ECMAScript Modules](#nodejs).
### Apps
Desktop and mobile apps have their own specific APIs covered in separate demos:
- [Electron and other desktop apps](/docs/demos/desktop)
- [React Native and other mobile apps](/docs/demos/mobile)