A simple, lightweight s3 client. Only 2 dependencies total.
Go to file
AJ ONeal 0d388cca6f
feat: throw if keys aren't valid-ish
2022-01-06 16:25:05 -07:00
bin v1.2.0: bugfix for buckets with invalid domain names, and pass request options 2021-01-26 16:57:20 -07:00
.gitignore v1.0.2: improve docs and error messages 2020-03-16 19:58:31 -06:00
.jshintrc chore: update linter and style config 2022-01-06 16:22:51 -07:00
.prettierrc.json chore: update linter and style config 2022-01-06 16:22:51 -07:00
LICENSE v1.0.0: a diet s3 client 2020-03-12 04:26:31 -06:00
README.md v1.2.0: bugfix for buckets with invalid domain names, and pass request options 2021-01-26 16:57:20 -07:00
package-lock.json chore: rename index.js => s3.js and update package.json deps 2022-01-06 16:03:36 -07:00
package.json chore: rename index.js => s3.js and update package.json deps 2022-01-06 16:03:36 -07:00
s3.js feat: throw if keys aren't valid-ish 2022-01-06 16:25:05 -07:00
test.bin v1.0.0: a diet s3 client 2020-03-12 04:26:31 -06:00
test.js make Prettier 2021-01-26 16:48:15 -07:00

README.md

s3.js | a Root project

Minimalist S3 client
(for AWS, Minio, Digital Ocean Spaces, etc)

A lightweight alternative to the S3 SDK that uses only @root/request and aws4.

  • set()
  • get()
  • head()
  • delete()
  • sign()

Download a file from S3

This library supports the same streaming options as @root/request.js.

as a stream

var resp = await s3.get({
    accessKeyId,        // 'AKIAXXXXXXXXXXXXXXXX'
    secretAccessKey,    // 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    region,             // 'us-east-2'
    bucket,             // 'bucket-name'
    prefix,             // 'my-prefix/' (optional)
    key,                // 'data/stats.csv' (omits prefix, if any)
    stream              // fs.createWriteStream('./path/to/file.bin')
});

await resp.stream;

in-memory

var resp = await s3.get({
    accessKeyId,        // 'AKIAXXXXXXXXXXXXXXXX'
    secretAccessKey,    // 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    region,             // 'us-east-2'
    bucket,             // 'bucket-name'
    prefix,             // 'my-prefix/' (optional)
    key                 // 'data/stats.csv' (omits prefix, if any)
});

fs.writeFile(resp.body, './path/to/file.bin');

Upload a new file to S3

await s3.set({
    accessKeyId,
    secretAccessKey,
    region,
    bucket,
    prefix,
    key,
    body,               // Buffer.from("hello, world")
                        // or fs.createReadStream("./file.txt")

    size                // (await fs.stat("./file.txt")).size (required for streams)
});

Check that a file exists

var resp = await s3.head({
    accessKeyId,        // 'AKIAXXXXXXXXXXXXXXXX'
    secretAccessKey,    // 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    region,             // 'us-east-2'
    bucket,             // 'bucket-name'
    prefix,             // 'my-prefix/' (optional)
    key                 // 'data/stats.csv' (omits prefix, if any)
});

console.log(resp.headers);

Delete file

var resp = await s3.delete({
    accessKeyId,        // 'AKIAXXXXXXXXXXXXXXXX'
    secretAccessKey,    // 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    region,             // 'us-east-2'
    bucket,             // 'bucket-name'
    prefix,             // 'my-prefix/' (optional)
    key                 // 'data/stats.csv' (omits prefix, if any)
});

console.log(resp.headers);

Return signed URL without fetching.

s3.sign({
    method: 'get',
    accessKeyId,
    secretAccessKey,
    region,
    bucket,
    prefix,
    key
});

A note on S3 terminology

bucket most similar to what most people think of as a "folder"
MUST NOT contain a slash /
key
("object key")
most similar to a "file name"
may contain "/"s as part of the name
MUST NOT BEGIN with a slash /
prefix an informal term, refers to "file path"
what the AWS console uses for created virtual folder-like views and searches
MUST END with a slash /

This library provides prefix (of key) for convenience.

s3://bucket-name/long/prefix/data/stats.csv can be represented equally well by any of the following:

(no prefix)

{
    "bucket": "bucket-name",
    "prefix": "",
    "key": "long/prefix/data/stats.csv"
}

(with long prefix)

{
    "bucket": "bucket-name",
    "prefix": "long/prefix/data/",
    "key": "stats.csv"
}

(with short prefix)

{
    "bucket": "bucket-name",
    "prefix": "long/",
    "key": "prefix/data/stats.csv"
}

Troubleshooting

If the body is a stream then size must be set to fs.statSync(filePath).size, or the request will fail:

501
<Code>NotImplemented</Code><Message>A header you provided implies functionality that is not implemented</Message>