---
title: Amazon Web Services
pagination_prev: demos/local/index
pagination_next: demos/extensions/index
---
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[Amazon Web Services](https://aws.amazon.com/) (AWS) is a Cloud Services
platform which includes traditional virtual machine support, "Serverless
Functions" and cloud storage.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo explores two key AWS offerings:
- ["Lambda Functions"](#lambda-functions) ("Lambda") explores the serverless
  computing offering. The demo creates a JavaScript function that can process
  user-submitted files and generate spreadsheets.
- ["S3 Storage"](#s3-storage) explores the cloud storage ("S3") offering. The
  demo uses the NodeJS connection library to read spreadsheets from S3 and write
  spreadsheets to a S3 bucket.
:::caution pass
AWS iterates quickly and there is no guarantee that the referenced services
will be available in the future.
:::
:::note
This demo was last tested on 2023 October 01.
:::
## Lambda Functions
AWS offers the NodeJS runtime for JavaScript serverless function.[^1]
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
required in Lambda functions. When deploying, the entire `node_modules` folder
can be added to the ZIP package.
:::note pass
In this demo, the "Function URL" (automatic API Gateway management) features
are used.  Older deployments required special "Binary Media Types" to handle
formats like XLSX.  At the time of testing, the configuration was not required.
:::
:::info pass
Node.js runtime can use `x86_64` or `arm64` CPU architectures. SheetJS libraries
work on both platforms in Linux, Windows, and macOS operating systems.
:::
### Reading Data
In the Lambda handler, the `event.body` attribute is a Base64-encoded string
representing the HTTP request form data. This body must be parsed.
#### Processing Form Bodies
The `busboy` body parser[^2] is battle-tested in NodeJS deployments.
`busboy` fires a `'file'` event for every file in the form body. The callback
receives a NodeJS stream that should be collected into a Buffer:
```js
/* accumulate the files manually */
var files = {};
bb.on('file', function(fieldname, file, filename) {
  /* concatenate the individual data buffers */
  var buffers = [];
  file.on('data', function(data) { buffers.push(data); });
  file.on('end', function() { files[fieldname] = Buffer.concat(buffers); });
});
```
`busboy` fires a `'finish'` event when the body parsing is finished. Callbacks
can assume every file in the form body has been stored in NodeJS Buffer objects.
#### Processing NodeJS Buffers
The SheetJS `read` method[^3] can read the Buffer objects and generate SheetJS
workbook objects[^4] which can be processed with other API functions.
For example, a handler can use `sheet_to_csv`[^5] to generate CSV text:
```js
/* on the finish event, all of the fields and files are ready */
bb.on('finish', function() {
  /* grab the first file */
  var f = files["upload"];
  if(!f) callback(new Error("Must submit a file for processing!"));
  /* f[0] is a buffer */
  // highlight-next-line
  var wb = XLSX.read(f[0]);
  /* grab first worksheet and convert to CSV */
  var ws = wb.Sheets[wb.SheetNames[0]];
  callback(null, { statusCode: 200, body: XLSX.utils.sheet_to_csv(ws) });
});
```
Complete Code Sample (click to show)
This example takes the first uploaded file submitted with the key `upload`,
parses the file and returns the CSV content of the first worksheet.
```js
const XLSX = require('xlsx');
var Busboy = require('busboy');
exports.handler = function(event, context, callback) {
  /* set up busboy */
  var ctype = event.headers['Content-Type']||event.headers['content-type'];
  var bb = Busboy({headers:{'content-type':ctype}});
  /* busboy is evented; accumulate the fields and files manually */
  var fields = {}, files = {};
  bb.on('error', function(err) { callback(null, { body: err.message }); });
  bb.on('field', function(fieldname, val) {fields[fieldname] = val });
  // highlight-start
  bb.on('file', function(fieldname, file, filename) {
    /* concatenate the individual data buffers */
    var buffers = [];
    file.on('data', function(data) { buffers.push(data); });
    file.on('end', function() { files[fieldname] = [Buffer.concat(buffers), filename]; });
  });
  // highlight-end
  /* on the finish event, all of the fields and files are ready */
  bb.on('finish', function() {
    /* grab the first file */
    var f = files["upload"];
    if(!f) callback(new Error("Must submit a file for processing!"));
    /* f[0] is a buffer */
    // highlight-next-line
    var wb = XLSX.read(f[0]);
    /* grab first worksheet and convert to CSV */
    var ws = wb.Sheets[wb.SheetNames[0]];
    callback(null, { statusCode: 200, body: XLSX.utils.sheet_to_csv(ws) });
  });
  /* start the processing */
  // highlight-next-line
  bb.end(Buffer.from(event.body, "base64"));
};
```
Complete Code Sample (click to show)
This example creates a sample workbook object and sends the file in the response:
```js
var XLSX = require('xlsx');
exports.handler = function(event, context, callback) {
  /* make workbook */
  var wb = XLSX.read("S,h,e,e,t,J,S\n5,4,3,3,7,9,5", {type: "binary"});
  /* write to XLSX file in Base64 encoding */
  // highlight-next-line
  var body = XLSX.write(wb, { type: "base64", bookType: "xlsx" });
  /* mark as attached file */
  var headers = { "Content-Disposition": 'attachment; filename="SheetJSLambda.xlsx"'};
  /* Send back data */
  callback(null, {
    statusCode: 200,
    // highlight-next-line
    isBase64Encoded: true,
    body: body,
    headers: headers
  });
};
```