---
title: Local File Access
---

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](../solutions/input)
- [Data Export](../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);
}
```

<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
// 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");
```

:::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_

In the `change` event of `<input type="file">`, `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.

:::

<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
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
<!-- load the shim script first -->
<script src="shim.min.js"></script>
<!-- then load the main script -->
<script src="xlsx.full.min.js"></script>
```

## 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](../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");
```

### 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");
```

### Deno

`readFile` uses `Deno.readFileSync` and `writeFile` uses `Deno.writeFileSync`:

```js
// @deno-types="https://cdn.sheetjs.com/xlsx-latest/package/types/index.d.ts"
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';

const wb = 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.

:::

### 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");
```

### Apps

Desktop and mobile apps have their own specific APIs covered in separate demos:

- [Electron and other desktop apps](./desktop)
- [React Native and other mobile apps](./mobile)

