forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			338 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			338 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | --- | ||
|  | sidebar_position: 26 | ||
|  | title: Local File Access | ||
|  | --- | ||
|  | 
 | ||
|  | Reading and writing files require native support.  `readFile` and `writeFile` | ||
|  | include support for some approaches but do not support every API.  When an API | ||
|  | is not supported by `readFile` or `writeFile`, the underlying `read` and | ||
|  | `write` methods can be used. | ||
|  | 
 | ||
|  | 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_ | ||
|  | 
 | ||
|  | Given a `Blob` or `File`, the underlying data cannot be read synchronously! | ||
|  | 
 | ||
|  | The callback-based approach uses a `FileReader`: | ||
|  | 
 | ||
|  | ```js | ||
|  | const reader = new FileReader(); | ||
|  | reader.onload = function(e) { | ||
|  |   /* e.target.result is an ArrayBuffer */ | ||
|  |   const wb = XLSX.read(e.target.result); | ||
|  |   console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])); | ||
|  | } | ||
|  | reader.readAsArrayBuffer(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" }); | ||
|  | ``` | ||
|  | 
 | ||
|  | ## Web Workers
 | ||
|  | 
 | ||
|  | :::warning | ||
|  | 
 | ||
|  | **None of the browser methods work from Web Worker contexts!** | ||
|  | 
 | ||
|  | Data operations with the Web APIs must happen in the browser main thread. | ||
|  | 
 | ||
|  | ::: | ||
|  | 
 | ||
|  | Web Workers and main thread can transfer `ArrayBuffer` or `Uint8Array` objects. | ||
|  | 
 | ||
|  | When generating a file, the worker will call `XLSX.write` with type `buffer` | ||
|  | and transfer the result to the main thread to initiate a download. | ||
|  | 
 | ||
|  | When parsing a file, the main thread will use the web API to read a `File` or | ||
|  | `Blob`, extract the underlying `ArrayBuffer` and transfer to the Web Worker. | ||
|  | 
 | ||
|  | ## HTML5 Download Attribute
 | ||
|  | 
 | ||
|  | _Writing Files_ | ||
|  | 
 | ||
|  | `writeFile` will attempt a download in the browser using the attribute. | ||
|  | 
 | ||
|  | ```js | ||
|  | XLSX.writeFile(wb, "SheetJS.xlsx"); | ||
|  | ``` | ||
|  | 
 | ||
|  | ## 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 | ||
|  | async function handleDropAsync(e) { | ||
|  |   e.stopPropagation(); e.preventDefault(); | ||
|  |   /* 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); | ||
|  | ``` | ||
|  | 
 | ||
|  | ## File System Access API
 | ||
|  | 
 | ||
|  | _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(); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Demo
 | ||
|  | 
 | ||
|  | <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'] } } ] | ||
|  |   }); | ||
|  |   console.log(wFile); | ||
|  |   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> | ||
|  | 
 | ||
|  | ## File and Directory Entries API
 | ||
|  | 
 | ||
|  | In the web browser, the File and Directory Entries API does not project to the | ||
|  | local file system. `cordova-plugin-file` *does* write to device in mobile apps! | ||
|  | 
 | ||
|  | _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`. `writeFile` will use the method. | ||
|  | 
 | ||
|  | #### 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 = XLSX.readFile("sheetjs.numbers"); | ||
|  | XLSX.writeFile(wb, "sheetjs.xls"); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Deno
 | ||
|  | 
 | ||
|  | `Deno.readFileSync` and `Deno.writeFileSync` are supported by `readFile` and | ||
|  | `writeFile` out of the box: | ||
|  | 
 | ||
|  | ```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"); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### 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) | ||
|  | 
 |