HTTP Uploads
Browsers and other platforms offer solutions for uploading files to servers and cloud storage solutions. Spreadsheets can be written using SheetJS and uploaded.
This demo explores file uploads using a number of browser APIs and wrapper libraries. The upload process will generate a sample XLSX workbook, upload the file to a test server, and display the response.
This demo focuses on uploading files. Other demos cover other HTTP use cases:
- "HTTP Downloads" covers downloading files
- "HTTP Server Processing" covers HTTP servers
Third-party cloud platforms such as AWS may corrupt raw binary uploads by encoding requests and responses in UTF-8 strings.
For AWS, in the "Binary Media Types" section of the API Gateway console, the
"multipart/form-data"
type should be added to ensure that AWS Lambda functions
can receive uploads from clients.
Uploading Binary Data
The SheetJS write
method1 generates file data stored in ArrayBuffer
objects. The ArrayBuffer
can be added to a FormData
object. The FormData
object can be passed along to POST requests.
Generating Files
In a typical scenario, a process generates arrays of simple objects.
The SheetJS json_to_sheet
method2 generates a SheetJS worksheet object3.
The book_new
method4 creates a workbook object that includes the worksheet.
The write
method5 generates the file in memory.
The following snippet creates a sample dataset and generates an ArrayBuffer
object representing the workbook bytes:
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
var ws = XLSX.utils.aoa_to_sheet(aoa);
var wb = XLSX.utils.book_new(ws, "Sheet1");
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
Creating Form Data
File
objects represent files. The File
constructor accepts an array of data
fragments and a filename.
Browser APIs typically represent form body data using FormData
objects. The
append
method adds fields to the FormData
object. Adding File
objects
effectively "attaches" a file in the upload.
The following snippet constructs a new FormData
object. The file
field in
the form will be set to the data from the previous snippet:
/* create File */
var file = new File([data], 'sheetjs.xlsx')
// generated XLSX ^^^^ ^^^^^^^^^^^^ file name
/* build FormData with the generated file */
var fdata = new FormData();
fdata.append('file', file);
// ^^^^ field name in the form body
POST Request
This demo explores a number of APIs and libraries for making POST requests. Each
approach will upload data stored in FormData
objects.
This snippet uses XMLHttpRequest
to upload data to https://s2c.sheetjs.com:
/* send data using XMLHttpRequest */
var req = new XMLHttpRequest();
req.open("POST", "https://s2c.sheetjs.com", true);
req.send(fdata);
Browser Demos
When the upload button is clicked, the browser will build up a new workbook, generate a XLSX file, upload it to https://s2c.sheetjs.com and show the response. If the process was successful, a HTML table will be displayed
Each browser demo was tested in the following environments:
Browser | Date |
---|---|
Chrome 126 | 2024-06-19 |
Safari 17.3 | 2024-06-19 |
Test Server
The https://s2c.sheetjs.com service is currently hosted on Deno Deploy. The "Deno Deploy" demo covers the exact steps for deploying the service.
The CORS-enabled service handles POST requests by looking for uploaded files in
the "file"
key. If a file is found, the file will be parsed using the SheetJS
read
method6 and the first worksheet will be converted to HTML using the
sheet_to_html
method7.
XMLHttpRequest
Using the XMLHttpRequest
API, the send
method can accept FormData
objects:
/* send data using XMLHttpRequest */
var req = new XMLHttpRequest();
req.open("POST", "https://s2c.sheetjs.com", true);
req.send(fdata);
Complete Code Snippet (click to show)
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
/* build FormData with the generated file */
var fdata = new FormData();
fdata.append('file', new File([data], 'sheetjs.xlsx'));
// field name ^^^^ file name ^^^^^^^^^^^^
/* send data using XMLHttpRequest */
var req = new XMLHttpRequest();
req.open("POST", "https://s2c.sheetjs.com", true);
req.send(fdata);
Live demo (click to show)
This demo starts from an array of arrays of data. When the button is clicked, a workbook file will be generated and uploaded to https://s2c.sheetjs.com. The service will return a HTML table.
function SheetJSXHRUL() { const [__html, setHTML] = React.useState(""); const [sz, setSz] = React.useState(0); const [csv, setCSV] = React.useState(""); /* raw data */ const aoa = [ ["S", "h", "e", "e", "t", "J", "S"], [ 5, 4, 3, 3, 7, 9, 5] ]; /* target URL */ const url = "https://s2c.sheetjs.com"; /* Fetch and update HTML */ const xport = React.useCallback(async() => { try { /* Make SheetJS Workbook from data */ const ws = XLSX.utils.aoa_to_sheet(aoa); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); /* Export to XLSX */ const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); setSz(data.length || data.byteLength); /* Make FormData */ const fdata = new FormData(); fdata.append('file', new File([data], 'sheetjs.xlsx')); /* Upload */ /* - create XMLHttpRequest */ const req = new XMLHttpRequest(); req.open("POST", url, true); /* - on success, display the contents */ req.onload = (e) => setHTML(req.responseText); /* - on error, display "Request failed" */ req.onerror = (e) => setHTML("Request failed"); /* - send data */ req.send(fdata); } catch(e) { setHTML(e && e.message || e); } }); /* Display data in CSV form */ React.useEffect(() => { const ws = XLSX.utils.aoa_to_sheet(aoa); setCSV(XLSX.utils.sheet_to_csv(ws)); }, []); return ( <pre><b>CSV Data</b><div>{csv}</div> {sz ? ( <> <b>Generated file size: {sz} bytes</b> <div dangerouslySetInnerHTML={{ __html }}/> </> ) : (<button onClick={xport}><b>Export and Upload!</b></button>)} </pre> ); }
fetch
fetch
takes a second parameter which allows for setting POST request body:
/* send data using fetch */
fetch("https://s2c.sheetjs.com", { method: "POST", body: fdata });
Complete Code Snippet (click to show)
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
/* build FormData with the generated file */
var fdata = new FormData();
fdata.append('file', new File([data], 'sheetjs.xlsx'));
// field name ^^^^ file name ^^^^^^^^^^^^
/* send data using fetch */
fetch("https://s2c.sheetjs.com", { method: "POST", body: fdata });
Live demo (click to show)
This demo uses fetch
to upload data to https://s2c.sheetjs.com. It will parse
the workbook and return an HTML table.
function SheetJSFetchUL() { const [__html, setHTML] = React.useState(""); const [sz, setSz] = React.useState(0); const [csv, setCSV] = React.useState(""); /* raw data */ const aoa = [ ["S", "h", "e", "e", "t", "J", "S"], [ 5, 4, 3, 3, 7, 9, 5] ]; /* target URL */ const url = "https://s2c.sheetjs.com"; /* Fetch and update HTML */ const xport = React.useCallback(async() => { try { /* Make SheetJS Workbook from data */ const ws = XLSX.utils.aoa_to_sheet(aoa); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); /* Export to XLSX */ const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); setSz(data.length || data.byteLength); /* Make FormData */ const fdata = new FormData(); fdata.append('file', new File([data], 'sheetjs.xlsx')); /* Upload */ const res = await fetch(url, {method:"POST", body: fdata}); /* Show Server Response */ setHTML((await res.text())); } catch(e) { setHTML(e && e.message || e); }}); /* Display data in CSV form */ React.useEffect(() => { const ws = XLSX.utils.aoa_to_sheet(aoa); setCSV(XLSX.utils.sheet_to_csv(ws)); }, []); return (<pre><b>CSV Data</b><div>{csv}</div> {sz ? ( <> <b>Generated file size: {sz} bytes</b> <div dangerouslySetInnerHTML={{ __html }}/> </> ) : (<button onClick={xport}><b>Export and Upload!</b></button>)} </pre>); }
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
axios
presents a Promise based interface.
Uploading form data is nearly identical to the fetch
example:
/* send data using axios */
axios("https://s2c.sheetjs.com", { method: "POST", body: fdata });
Complete Code Snippet (click to show)
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
/* build FormData with the generated file */
var fdata = new FormData();
fdata.append('file', new File([data], 'sheetjs.xlsx'));
// field name ^^^^ file name ^^^^^^^^^^^^
/* send data using axios */
axios("https://s2c.sheetjs.com", { method: "POST", data: fdata });
Live demo (click to show)
This demo uses axios
to upload data to https://s2c.sheetjs.com. It will parse
the workbook and return an HTML table.
If the live demo shows a message
ReferenceError: axios is not defined
please refresh the page. This is a known bug in the documentation generator.
function SheetJSAxiosUL() { const [__html, setHTML] = React.useState(""); const [sz, setSz] = React.useState(0); const [csv, setCSV] = React.useState(""); /* raw data */ const aoa = [ ["S", "h", "e", "e", "t", "J", "S"], [ 5, 4, 3, 3, 7, 9, 5] ]; /* target URL */ const url = "https://s2c.sheetjs.com"; /* Fetch and update HTML */ const xport = React.useCallback(async() => { try { /* Make SheetJS Workbook from data */ const ws = XLSX.utils.aoa_to_sheet(aoa); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); /* Export to XLSX */ const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); setSz(data.length || data.byteLength); /* Make FormData */ const fdata = new FormData(); fdata.append('file', new File([data], 'sheetjs.xlsx')); /* Upload */ const res = await axios(url, {method:"POST", data: fdata}); /* Show Server Response */ setHTML(res.data); } catch(e) { setHTML(e && e.message || e); }}); /* Display data in CSV form */ React.useEffect(() => { const ws = XLSX.utils.aoa_to_sheet(aoa); setCSV(XLSX.utils.sheet_to_csv(ws)); }, []); return (<pre><b>CSV Data</b><div>{csv}</div> {sz ? ( <> <b>Generated file size: {sz} bytes</b> <div dangerouslySetInnerHTML={{ __html }}/> </> ) : (<button onClick={xport}><b>Export and Upload!</b></button>)} </pre>); }
superagent
superagent
is a network request library
with a "Fluent Interface".
The send
method accepts a FormData
object as the first argument:
/* send data using superagent */
superagent.post("https://s2c.sheetjs.com").send(fd);
Complete Code Snippet (click to show)
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
/* build FormData with the generated file */
var fdata = new FormData();
fdata.append('file', new File([data], 'sheetjs.xlsx'));
// field name ^^^^ file name ^^^^^^^^^^^^
/* send data (fd is the FormData object) */
superagent.post("https://s2c.sheetjs.com").send(fd);
Live demo (click to show)
This demo uses superagent
to upload data to https://s2c.sheetjs.com. It will
parse the workbook and return an HTML table.
If the live demo shows a message
ReferenceError: superagent is not defined
please refresh the page. This is a known bug in the documentation generator.
function SheetJSSuperAgentUL() { const [__html, setHTML] = React.useState(""); const [sz, setSz] = React.useState(0); const [csv, setCSV] = React.useState(""); /* raw data */ const aoa = [ ["S", "h", "e", "e", "t", "J", "S"], [ 5, 4, 3, 3, 7, 9, 5] ]; /* target URL */ const url = "https://s2c.sheetjs.com"; /* Fetch and update HTML */ const xport = React.useCallback(async() => { try { /* Make SheetJS Workbook from data */ const ws = XLSX.utils.aoa_to_sheet(aoa); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); /* Export to XLSX */ const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'}); setSz(data.length || data.byteLength); /* Make FormData */ const fdata = new FormData(); fdata.append('file', new File([data], 'sheetjs.xlsx')); /* Upload */ superagent.post(url).send(fdata).end((err, res) => { /* Show Server Response */ setHTML(res.text); }); } catch(e) { setHTML(e && e.message || e); }}); /* Display data in CSV form */ React.useEffect(() => { const ws = XLSX.utils.aoa_to_sheet(aoa); setCSV(XLSX.utils.sheet_to_csv(ws)); }, []); return (<pre><b>CSV Data</b><div>{csv}</div> {sz ? ( <> <b>Generated file size: {sz} bytes</b> <div dangerouslySetInnerHTML={{ __html }}/> </> ) : (<button onClick={xport}><b>Export and Upload!</b></button>)} </pre>); }
NodeJS Demos
These examples show how to upload data in NodeJS.
fetch
NodeJS fetch
, available in version 20, mirrors the browser fetch
.
This demo was tested in the following environments:
NodeJS | Date |
---|---|
20.12.1 | 2024-04-07 |
21.7.2 | 2024-04-07 |
Complete Example (click to show)
This demo uses fetch
to upload data to https://s2c.sheetjs.com. It will parse
the workbook and return data in CSV rows.
- Install the SheetJS NodeJS module:
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
- Save the following to
SheetJSFetch.js
:
const XLSX = require("xlsx");
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
/* build FormData with the generated file */
var fdata = new FormData();
fdata.append('file', new File([data], 'sheetjs.xlsx'));
// field name ^^^^ file name ^^^^^^^^^^^^
fdata.append('type', 'csv');
(async() => {
/* send data using fetch */
const res = await fetch("https://s2c.sheetjs.com", { method: "POST", body: fdata });
const txt = await res.text();
console.log(txt);
})();
- Run the script:
node SheetJSFetch.js
It will print CSV contents of the test file.
request
The deprecated request
library is useful
in legacy NodeJS deployments where fetch
may not be available.
The SheetJS write
method will generate NodeJS Buffer objects when the type
option is set to "buffer"
:
/* export SheetJS workbook object to XLSX file bytes */
const data = XLSX.write(wb, {bookType: 'xlsx', type: 'buffer'});
A request
file object can be built using the Buffer. The file object must
include an options
object that specifies the file name and content type:
/* create a file object for the `request` form data */
const request_file = {
/* `value` can be a Buffer object */
value: data,
options: {
/* `options.filename` is the filename that the server will see */
filename: "sheetjs.xlsx",
/* `options.contentType` must be set */
contentType: "application/octet-stream"
}
};
The request
and request.post
methods accept an options argument. The
formData
property specifies the body to be uploaded. Property names correspond
to the uploaded form names and values describe the uploaded content.
The request
file object should be added to the formData
object:
request({
// ... other options ...
formData: {
// ... other form fields ...
/* the server will see the uploaded file in the `file` body property */
file: request_file
}
}, function(err, res) { /* handle response ... */ });
This demo was tested in the following environments:
NodeJS | request | Date |
---|---|---|
0.10.48 | 2.88.2 | 2024-04-07 |
0.12.18 | 2.88.2 | 2024-04-07 |
4.9.1 | 2.88.2 | 2024-04-07 |
6.17.1 | 2.88.2 | 2024-04-07 |
8.17.0 | 2.88.2 | 2024-04-07 |
10.24.1 | 2.88.2 | 2024-04-07 |
12.22.12 | 2.88.2 | 2024-04-07 |
14.21.3 | 2.88.2 | 2024-04-07 |
16.20.2 | 2.88.2 | 2024-04-07 |
18.20.1 | 2.88.2 | 2024-04-07 |
20.12.1 | 2.88.2 | 2024-04-07 |
Complete Example (click to show)
This demo uses request
to upload data to https://s2c.sheetjs.com. It will
parse the workbook and return data in CSV rows.
- Install the SheetJS NodeJS module
and
request
module:
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz request
- Save the following to
SheetJSRequest.js
:
const XLSX = require("xlsx");
const request = require("request");
/* create sample SheetJS workbook object */
var aoa = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
const ws = XLSX.utils.aoa_to_sheet(aoa);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* export SheetJS workbook object to XLSX file bytes */
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'buffer'});
request({
method: "POST",
url: "https://s2c.sheetjs.com",
headers: {
Accept: "text/html"
},
formData: {
type: "csv",
file: {
value: data,
options: {
filename: "sheetjs.xlsx",
contentType: "application/octet-stream"
}
}
}
}, function(err, res, body) {
if(err) return console.error(err);
console.log(body);
});
- Run the script:
node SheetJSRequest.js
It will print CSV contents of the test file.
For legacy versions of NodeJS, the process may fail with a certificate error:
{ [Error: certificate not trusted] code: 'CERT_UNTRUSTED' }
The environment variable NODE_TLS_REJECT_UNAUTHORIZED
can be set to 0
:
env NODE_TLS_REJECT_UNAUTHORIZED="0" node SheetJSRequest.js
It is strongly recommended to upgrade to a newer version of NodeJS!
Troubleshooting
Some SheetJS users have reported corrupted files. To diagnose the error, it is strongly recommended to write local files.
For example, using fetch
in the browser, the bytes can be downloaded using the
HTML5 Download Attribute. The
highlighted lines should be added immediately after write
:
/* Generate XLSX file */
const data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
/* Write to Local File */
const blob = new Blob([data]);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.download = "SheetJS.xlsx";
a.href = url;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
/* Make FormData */
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});
If the generated file is valid, then the issue is in the server infrastructure.
Footnotes
-
See "Worksheet Object" in "SheetJS Data Model" for more details. ↩