--- 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(blob); /* 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)