From a04ceb6bfb77891a860472a82eeb9f658a2e10ed Mon Sep 17 00:00:00 2001 From: SheetJS Date: Fri, 5 Aug 2022 01:10:11 -0400 Subject: [PATCH] network --- .../03-demos/06-network.mdx | 481 ++++++++++++++++++ .../{06-clipboard.md => 17-clipboard.md} | 2 +- .../docs/04-getting-started/03-demos/index.md | 2 +- docz/docs/06-solutions/01-input.md | 2 +- docz/docs/06-solutions/05-output.md | 2 +- 5 files changed, 485 insertions(+), 4 deletions(-) create mode 100644 docz/docs/04-getting-started/03-demos/06-network.mdx rename docz/docs/04-getting-started/03-demos/{06-clipboard.md => 17-clipboard.md} (96%) diff --git a/docz/docs/04-getting-started/03-demos/06-network.mdx b/docz/docs/04-getting-started/03-demos/06-network.mdx new file mode 100644 index 0000000..16fdca6 --- /dev/null +++ b/docz/docs/04-getting-started/03-demos/06-network.mdx @@ -0,0 +1,481 @@ +--- +sidebar_position: 6 +title: HTTP Network Requests +--- + + + + + + +`XMLHttpRequest` and `fetch` browser APIs enable binary data transfer between +web browser clients and web servers. Since this library works in web browsers, +server conversion work can be offloaded to the client! This demo shows a few +common scenarios involving browser APIs and popular wrapper libraries. + +:::caution Third-Party Hosts and Binary Data + +Some services like AWS will corrupt raw binary uploads / downloads by encoding +requests and responses in UTF-8. Typically, these services have options for +disabling this behavior. + +For AWS, in the "Binary Media Types" section of the API Gateway console, the +following types should be added to ensure smooth uploads and downloads: + +- `"multipart/form-data"` (for Lambda functions to receive files from clients) +- `"application/vnd.ms-excel"` (for Lambda functions to send files to clients) + +::: + +## Downloading Binary Data + +Most interesting spreadsheet files are binary data that contain byte sequences +that represent invalid UTF-8 characters. + +The APIs generally have a way to control the interpretation of the downloaded +data. The `arraybuffer` response type usually forces the data to be presented +as a pure `ArrayBuffer` which can be parsed directly with `XLSX.read`. + +For example, with `fetch`: + +```js +const res = await fetch("https://sheetjs.com/pres.numbers"); +const ab = res.arrayBuffer(); // recover data as ArrayBuffer + +const wb = XLSX.read(ab); +``` + +## Uploading Binary Data + +`FormData` objects can hold `File` blobs generated from `XLSX.write`: + +```js +/* generate XLSX file bytes */ +var data = XLSX.write(workbook, {bookType: 'xlsx', type: 'array'}); + +/* build FormData with the generated file */ +var fdata = new FormData(); +fdata.append('data', new File([data], 'sheetjs.xlsx')); +// field name ^^^^ file name ^^^^^^^^^^^^ +``` + +The `FormData` object can be passed along to the POST request. For example: + +```js +var req = new XMLHttpRequest(); +req.open("POST", "/upload", true); +req.send(fdata); +``` + +## Browser Demos + +The included demos focus on an editable table. There are two separate flows: + +- When the page is accessed, the browser will attempt to download + and read the workbook. The old table will be replaced with an editable table + whose contents match the first worksheet. The table is generated using the + `sheet_to_html` utility with `editable:true` option + +- When the upload button is clicked, the browser will generate a new worksheet + using `table_to_book` and build up a new workbook. It will then attempt to + generate a file, upload it to and show the response. + +### XMLHttpRequest + +For downloading data, the `arraybuffer` response type generates an `ArrayBuffer` +that can be viewed as an `Uint8Array` and fed to `XLSX.read` using `array` type: + +```js +/* set up an async GET request */ +var req = new XMLHttpRequest(); +req.open("GET", url, true); +req.responseType = "arraybuffer"; + +req.onload = function(e) { + /* parse the data when it is received */ + var data = new Uint8Array(req.response); + var workbook = XLSX.read(data, {type:"array"}); + /* DO SOMETHING WITH workbook HERE */ +}; +req.send(); +``` + +
Live Download demo (click to show) + +This demo uses `XMLHttpRequest` to download +and show the data in an HTML table. + +```jsx live +function SheetJSXHRDL() { + const [html, setHTML] = React.useState(""); + + /* Fetch and update HTML */ + React.useEffect(async() => { + /* Fetch file */ + const req = new XMLHttpRequest(); + req.open("GET", "https://sheetjs.com/pres.numbers", true); + req.responseType = "arraybuffer"; + req.onload = e => { + /* Parse file */ + const wb = XLSX.read(new Uint8Array(req.response)); + const ws = wb.Sheets[wb.SheetNames[0]]; + + /* Generate HTML */ + setHTML(XLSX.utils.sheet_to_html(ws)); + }; + req.send(); + }, []); + + return (
); +} +``` + +
+ +For uploading data, this demo populates a `FormData` object with an ArrayBuffer +generated with the `array` output type: + +```js +/* generate XLSX as array buffer */ +var data = XLSX.write(workbook, {bookType: 'xlsx', type: 'array'}); + +/* build FormData with the generated file */ +var fd = new FormData(); +fd.append('data', new File([data], 'sheetjs.xlsx')); + +/* send data */ +var req = new XMLHttpRequest(); +req.open("POST", url, true); +req.send(fd); +``` + +
Live Upload demo (click to show) + +This demo uses `XMLHttpRequest` to upload data to . It +will parse the workbook and return an HTML table. + +```jsx live +function SheetJSXHRUL() { + const [html, setHTML] = React.useState(""); + const [sz, setSz] = React.useState(0); + const csv = "a,b,c\n1,2,3"; + /* Fetch and update HTML */ + const xport = React.useCallback(async() => { + /* Make Workbook from CSV */ + const wb = XLSX.read(csv, { type: "string" }); + + /* Make FormData */ + const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); + setSz(data.length || data.byteLength); + const fdata = new FormData(); + fdata.append('file', new File([data], 'sheetjs.xlsx')); + + /* Upload */ + const url = "https://s2c.sheetjs.com"; + const req = new XMLHttpRequest(); + req.open("POST", url, true); + req.onload = (e) => setHTML(req.responseText); + req.send(fdata); + }); + + return (
+    CSV Data
+    
{csv}
+ {sz ? (<> + Generated file size: {sz} bytes +
+ ) : ()} +
); +} +``` + +
+ +### fetch + +For downloading data, `Response#arrayBuffer` resolves to an `ArrayBuffer` that +can be converted to `Uint8Array` and passed to `XLSX.read`: + +```js +fetch(url).then(function(res) { + /* get the data as a Blob */ + if(!res.ok) throw new Error("fetch failed"); + return res.arrayBuffer(); +}).then(function(ab) { + /* parse the data when it is received */ + var data = new Uint8Array(ab); + var workbook = XLSX.read(data, {type:"array"}); + + /* DO SOMETHING WITH workbook HERE */ +}); +``` + +
Live Download demo (click to show) + +This demo uses `fetch` to download and show +the data in an HTML table. + +```jsx live +function SheetJSFetchDL() { + const [html, setHTML] = React.useState(""); + + /* Fetch and update HTML */ + React.useEffect(async() => { + /* Fetch file */ + const res = await fetch("https://sheetjs.com/pres.numbers"); + const ab = await res.arrayBuffer(); + + /* Parse file */ + const wb = XLSX.read(ab); + const ws = wb.Sheets[wb.SheetNames[0]]; + + /* Generate HTML */ + setHTML(XLSX.utils.sheet_to_html(ws)); + }, []); + + return (
); +} +``` + +
+ +`fetch` takes a second parameter which allows for setting POST request body: + +```js +// assuming `fdata` is a FormData object from "Uploading Binary Data" section +fetch("/upload", { method: "POST", body: fdata }); +``` + +
Live Upload demo (click to show) + +This demo uses `fetch` to upload data to . It will parse +the workbook and return an HTML table. + +```jsx live +function SheetJSFetchUL() { + const [html, setHTML] = React.useState(""); + const [sz, setSz] = React.useState(0); + const csv = "a,b,c\n1,2,3"; + /* Fetch and update HTML */ + const xport = React.useCallback(async(e) => { + /* Make Workbook from CSV */ + const wb = XLSX.read(csv, { type: "string" }); + const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); + + /* Make FormData */ + setSz(data.length || data.byteLength); + const fdata = new FormData(); + fdata.append('file', new File([data], 'sheetjs.xlsx')); + + /* Upload */ + const url = "https://s2c.sheetjs.com"; + const res = await fetch(url, {method:"POST", body: fdata}); + + /* Set HTML */ + setHTML((await res.text())); + }); + + return (
+    CSV Data
+    
{csv}
+ {sz ? (<> + Generated file size: {sz} bytes +
+ ) : ()} +
); +} +``` + +
+ + +### Wrapper Libraries + +Before `fetch` shipped with browsers, there were various wrapper libraries to +simplify `XMLHttpRequest`. Due to limitations with `fetch`, these libraries +are still relevant. + +#### axios + +The `axios` library presents a Promise interface. Setting `responseType` to +`arraybuffer` ensures the return type is an ArrayBuffer: + +```js +async function workbook_dl_axios(url) { + const res = await axios(url, {responseType:'arraybuffer'}); + const workbook = XLSX.read(res.data); + return workbook; +} +``` + +
Live Download demo (click to show) + +This demo uses `axios` to download and show +the data in an HTML table. + +```jsx live +function SheetJSAxiosDL() { + const [html, setHTML] = React.useState(""); + + /* Fetch and update HTML */ + React.useEffect(async() => { + /* Fetch file */ + const res = await axios("https://sheetjs.com/pres.numbers", {responseType: "arraybuffer"}); + + /* Parse file */ + const wb = XLSX.read(res.data); + const ws = wb.Sheets[wb.SheetNames[0]]; + + /* Generate HTML */ + setHTML(XLSX.utils.sheet_to_html(ws)); + }, []); + + return (
); +} +``` + +
+ +Uploading form data is nearly identical to the `fetch` example: + +```js +axios("/upload", { method: "POST", data: fdata }); +``` + +
Live Upload demo (click to show) + +This demo uses `axios` to upload data to . It will parse +the workbook and return an HTML table. + +```jsx live +function SheetJSAxiosUL() { + const [html, setHTML] = React.useState(""); + const [sz, setSz] = React.useState(0); + const csv = "a,b,c\n1,2,3"; + /* Fetch and update HTML */ + const xport = React.useCallback(async() => { + /* Make Workbook from CSV */ + const wb = XLSX.read(csv, { type: "string" }); + + /* Make FormData */ + const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); + setSz(data.length || data.byteLength); + const fdata = new FormData(); + fdata.append('file', new File([data], 'sheetjs.xlsx')); + + /* Upload */ + const url = "https://s2c.sheetjs.com"; + const res = await axios(url, {method:"POST", data: fdata}); + + /* Set HTML */ + setHTML(res.data); + }); + + return (
+    CSV Data
+    
{csv}
+ {sz ? (<> + Generated file size: {sz} bytes +
+ ) : ()} +
); +} +``` + +
+ +### superagent Wrapper Library + +The `superagent` library usage mirrors XHR: + +```js +/* set up an async GET request with superagent */ +superagent.get(url).responseType('arraybuffer').end(function(err, res) { + /* parse the data when it is received */ + var data = new Uint8Array(res.body); + var workbook = XLSX.read(data, {type:"array"}); + + /* DO SOMETHING WITH workbook HERE */ +}); +``` + +
Live Download demo (click to show) + +This demo uses `superagent` to download and +show the data in an HTML table. + +```jsx live +function SheetJSSuperAgentDL() { + const [html, setHTML] = React.useState(""); + + /* Fetch and update HTML */ + React.useEffect(async() => { + /* Fetch file */ + superagent + .get("https://sheetjs.com/pres.numbers") + .responseType("arraybuffer") + .end((err, res) => { + /* Parse file */ + const wb = XLSX.read(res.body); + const ws = wb.Sheets[wb.SheetNames[0]]; + + /* Generate HTML */ + setHTML(XLSX.utils.sheet_to_html(ws)); + }); + }, []); + + return (
); +} +``` + +
+ +The upload portion only differs in the actual request command: + +```js +/* send data (fd is the FormData object) */ +superagent.post("/upload").send(fd); +``` + +
Live Upload demo (click to show) + +This demo uses `superagent` to upload data to . It will +parse the workbook and return an HTML table. + +```jsx live +function SheetJSSuperAgentUL() { + const [html, setHTML] = React.useState(""); + const [sz, setSz] = React.useState(0); + const csv = "a,b,c\n1,2,3"; + /* Fetch and update HTML */ + const xport = React.useCallback(async() => { + /* Make Workbook from CSV */ + const wb = XLSX.read(csv, { type: "string" }); + + /* Make FormData */ + const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); + setSz(data.length || data.byteLength); + const fdata = new FormData(); + fdata.append('file', new File([data], 'sheetjs.xlsx')); + + /* Upload */ + const url = "https://s2c.sheetjs.com"; + superagent.post(url).send(fdata).end((err, res) => { + /* Set HTML */ + setHTML(res.text); + }); + + }); + + return (
+    CSV Data
+    
{csv}
+ {sz ? (<> + Generated file size: {sz} bytes +
+ ) : ()} +
); +} +``` + +
diff --git a/docz/docs/04-getting-started/03-demos/06-clipboard.md b/docz/docs/04-getting-started/03-demos/17-clipboard.md similarity index 96% rename from docz/docs/04-getting-started/03-demos/06-clipboard.md rename to docz/docs/04-getting-started/03-demos/17-clipboard.md index 2c9bedd..2bfbe84 100644 --- a/docz/docs/04-getting-started/03-demos/06-clipboard.md +++ b/docz/docs/04-getting-started/03-demos/17-clipboard.md @@ -1,5 +1,5 @@ --- -sidebar_position: 6 +sidebar_position: 17 --- # Clipboard Data diff --git a/docz/docs/04-getting-started/03-demos/index.md b/docz/docs/04-getting-started/03-demos/index.md index 2f4cff6..222371c 100644 --- a/docz/docs/04-getting-started/03-demos/index.md +++ b/docz/docs/04-getting-started/03-demos/index.md @@ -9,7 +9,7 @@ The demo projects include small runnable examples and short explainers. ### JavaScript APIs -- [`XMLHttpRequest and fetch`](https://github.com/SheetJS/SheetJS/tree/master/demos/xhr/) +- [`XMLHttpRequest and fetch`](./network) - [`Clipboard Data`](./clipboard) - [`Typed Arrays for Machine Learning`](./ml) - [`LocalStorage and SessionStorage`](./database#localstorage-and-sessionstorage) diff --git a/docz/docs/06-solutions/01-input.md b/docz/docs/06-solutions/01-input.md index a55b581..48804dd 100644 --- a/docz/docs/06-solutions/01-input.md +++ b/docz/docs/06-solutions/01-input.md @@ -463,7 +463,7 @@ req.onload = function(e) { req.send(); ``` -The [`xhr` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/xhr/) includes a longer discussion and more examples. +The [`xhr` demo](../getting-started/demos/network) includes a longer discussion and more examples. shows fallback approaches for IE6+. diff --git a/docz/docs/06-solutions/05-output.md b/docz/docs/06-solutions/05-output.md index f8fecc3..646047b 100644 --- a/docz/docs/06-solutions/05-output.md +++ b/docz/docs/06-solutions/05-output.md @@ -489,7 +489,7 @@ is to adjust the server process or Lambda function to accept Base64 strings. ::: -A complete example using XHR is [included in the XHR demo](https://github.com/SheetJS/SheetJS/tree/master/demos/xhr/), along +A complete example using XHR is [included in the XHR demo](../getting-started/demos/network), along with examples for fetch and wrapper libraries. This example assumes the server can handle Base64-encoded files (see the demo for a basic nodejs server):