docs.sheetjs.com/docz/docs/03-demos/08-local/01-file.md
2023-08-29 23:44:38 -04:00

15 KiB

title pagination_prev pagination_next sidebar_custom_props
Local File Access demos/data/index demos/cloud/index
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 pass

Some snippets are also available in the "Common Use Cases" section:

:::

Web Browsers

:::warning pass

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:

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

// 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 includes a live demo.

IE10 Binary Strings (click to show)

XLSX.read supports binary strings with type: "binary":

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

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

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.

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:

/* 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" includes a live demo.

:::

File API

Reading Files

In the change event of <input type="file">, target hold a list of files:

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:

/* 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.

function SheetJSRoundTripFileSystemAPI() { return window.showSaveFilePicker ? (
<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>
  ) : ( <b>This browser does not support File System Access API</b> ); }

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:

/* 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:

/* 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

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

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

var XLSX = require("xlsx");
var wb = XLSX.readFile("sheetjs.numbers");
XLSX.writeFile(wb, "sheetjs.xls");

Installation has a special note for use with NodeJS ECMAScript Modules:

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

var fs = require("fs");
var buf = fs.readFileSync(filepath);
var wb = XLSX.read(buf);

ECMAScript Modules

import { read } from "xlsx";
import { readFileSync } from "fs";

var buf = readFileSync(filepath);
var wb = read(buf);

XLSX.writeFile(wb, filepath) is equivalent to:

CommonJS

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

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:

#include "xlsx.extendscript.js"

var wb = XLSX.readFile("sheetjs.xlsx");
XLSX.writeFile(wb, "sheetjs.csv");

The ExtendScript demo 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 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:

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.

Apps

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