AWS SDK S3 client for JavaScript

Some experience with the AWS SDK S3 client for JavaScript

Published on 20 Nov, 2021


aws s3 logo

I recently worked with the AWS SDK S3 client for JavaScript and I don’t know, but I feel like good examples and instructions regarding the functions, that the S3 client offers and what pitfalls are, are missing.

In this article I only cover functions on objects. In the perspective of S3 files and folders are both objects, and folder is an object that contains in its key a /.

First of all, there are two maintained versions of the AWS SDK, version 2 and version 3.
It is a bit confusing, because on the documentation page of version 2 they sometimes refer to latest version, which however means the last version of version 2.
Version 3 moved to a new documentation page.

Also some links in the version 3 documentation refer to the version 2 documentation so be aware of it, and maybe search manual for informations in the version 3 documentation.

First of all installation

To use the aws-sdk you have to install it, here is the npm repository link.

npm install --save @aws-sdk/client-s3

Changes in rollup

TL;DR: You may not have this issue, it can relate to my enviroment. By the way, I am using svelte and rollup for building and bundling.

I had some problems with a crypto module that the AWS SDK is using. I got an error that the variable exports is not defined.

I used already the commonjs plugin, but it doesn’t help.

Uncaught ReferenceError: exports is not defined
    at CryptoOperation.js:2
    at main.ts:25

After a while and with help we found a dirty fix, I adjusted my rollup.conf.js and add a replace statement to define exports.

replace(
  {
    Object.defineProperty(exports, "__esModule", { value: true });': '',
    delimiters: ['\n', '\n']
  },
),

Rename a s3 object

As there is no rename or move method on the s3 client, you have to copy and delete the old file (seriously no joke).

Be aware that the copy will increase your used storage and can be costful.

It could look like this.

import { S3Client, CopyObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';

...

const cpCommand:CopyObjectCommand = new CopyObjectCommand({
  Bucket: bucketName, Key: newObjectKey, 
  CopySource: bucketName + '/' + oldObjectKey
});

const deleteCommand:DeleteObjectCommand = new DeleteObjectCommand({
  Bucket: bucketName, 
  Key: oldObjectKey
});
await s3Client.send(cpCommand);
await s3Client.send(deleteCommand);

List top level objects in current folder

If you just want to list the objects in the current folder, you have specify / as delimiter in the ListObjectsV2Command, but then folders will not more contained in Contents attribute of the response, but in the CommonPrefixes attribute.

Upload file

To upload files there is an extra library aws-lib-storage.
To use this library I needed to add nodePolyfills.

import nodePolyfills from 'rollup-plugin-polyfill-node';

...

plugins: [
...
  nodePolyfills({ include: 'node_modules/@aws-sdk/lib-storage/**/*.js' }),
...
],

Just see the example on npmjs @aws-sdk/lib-storage.

Delete files

If you want to delete a folder with content, you first have to delete the content of the folder.
For this the command DeleteObjectsCommand exists. Search for all objects inside a folder and give the list to this command.

const listCommand: ListObjectsV2Command = new ListObjectsV2Command({
  Bucket: bucket,
  Prefix: 'folder/',
});
const objectsInFolder:ListObjectsV2CommandOutput = s3Client.send(listCommand);

let objectsToDelete: ObjectIdentifier[] = [];
for (let obj of objectsInFolder.Contents) {
  objectsToDelete.push({ Key: String(obj.Key) });
}
const deleteObjectsCommand: DeleteObjectsCommand = new DeleteObjectsCommand({
  Bucket: bucket,
  Delete: { Objects: objectsToDelete },
});
await s3Client.send(deleteObjectsCommand);

After you have done this, just delete the folder.

const command: DeleteObjectCommand = new DeleteObjectCommand({
  Bucket: bucket,
  Key: 'folder/',
});
await s3Client.send(command);

Search files

Sadly there is no full text search, you can just search for a prefix with the ListObjectsV2Command.
Like here:

const command: ListObjectsV2Command = new ListObjectsV2Command({
  Bucket: bucketName,
  Delimiter: '/',
  Prefix: searchPrefix,
});

const foundObjects = await s3Client.send(command);

Download files

Download files from s3 is pretty easy, but you need to use a signedUrl.

import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
...
async function downloadObject(object: any) {
  const command: GetObjectCommand = new GetObjectCommand({
    Bucket: bucketName,
    Key: getPrefixForKey(object.Key),
    ResponseContentDisposition: 'attachment',
  });
  const url = await getSignedUrl(s3Client, command, { expiresIn: 666 });
  
  const link = document.createElement('a');
  link.href = url;
  link.download = name;
  document.body.appendChild(link);
  link.dispatchEvent(
    new MouseEvent('click', {
      bubbles: true,
      cancelable: true,
      view: window,
    }),
  );
  document.body.removeChild(link);
}