s3-api

2.0.11 • Public • Published

Overview

The s3-api module provides a simple, light wrapper around the AWS S3 API (version 3). It greatly simplifies things like uploading and downloading files to/from S3, as well as treating it like a key/value store.

Features

  • Uses AWS SDK v3.
  • Fully async/await, with support for classic callbacks.
  • Use S3 as a key/value store.
  • Use JSON, buffers, streams or files.
  • Upload or download multiple files or entire directories recursively.
  • Optional gzip compression and decompression for files and streams.
  • Automatically handles uploading files using multipart chunks.
  • Automatically handles pagination when listing files.
  • Automatic retries with exponential backoff.
  • Logging and perf helpers.
  • Optional caching layer for JSON files.
  • Progress callback for most API calls.
  • Full-featured command-line interface (CLI).

Table of Contents

The documentation is split up across these files:

Here is the table of contents for this document:

Setup

Use npm to install the module locally:

npm install s3-api

Install the module globally to use the CLI:

npm install -g s3-api

API Usage

To use the API in your code, require the module, and instantiate a class:

const S3 = require('s3-api');

let s3 = new S3({
	credentials: {
		accessKeyId: "YOUR_ACCESS_KEY_HERE",
		secretAccessKey: "YOUR_SECRET_KEY_HERE"
	},
	bucket: 'my-bucket',
	prefix: 'myapp/data/'
});

The class constructor expects an object, which accepts several different properties (see below). At the very least you should specify a bucket and a prefix. You may also need to specify credentials as well, depending on your setup. The prefix is prepended onto all S3 keys, and is a great way to keep your app's S3 data in an isolated area when sharing a bucket.

Once you have your class instance created, call one of the available API methods (see API Reference for list). Example:

try {
	let result = await s3.uploadFile({ localFile: '/path/to/image.gif', key: 's3dir/myfile.gif' });
	// `result.meta` will be the metadata object from S3
}
catch(err) {
	// handle error here
}

The result object's properties will vary based on the API call. In the examples below, the result is destructed into local variables using the let {...} = syntax. This is known as destructuring assignment. Example:

try {
	let { files, bytes } = await s3.list({ remotePath: 'mydir' });
	// `files` will be an array of file objects, each with `key`, `size` and `mtime` props.
	// `bytes` is the total bytes of all listed files.
}
catch(err) {
	// handle error here
}

Please note that the local variables must be named exactly as shown above (e.g. files, bytes in this case), as they are being yanked from an object. You can omit specific variables if you don't care about them, e.g. let { files } = await ... (omitting bytes). If you don't want to declare new local variables for the object properties, just use the let result = await ... syntax instead.

It is highly recommended that you instantiate the S3 API class one time, and reuse it for the lifetime of your application. The reason is, the library reuses network connections to reduce S3 lag. Each time you instantiate a new class it has to open new connections.

Key Value Store

If you want to use S3 as a key/value store, then this is the library for you. The put() and get() API calls store and fetch objects, serialized to/from JSON behind the scenes. Example:

try {
	// store a record
	await s3.put({ key: 'users/kermit', value: { animal: 'frog', color: 'green' } });
	
	// fetch a record
	let { data } = await s3.get({ key: 'users/kermit' });
	console.log(data); // { "animal": "frog", "color": "green" }
}
catch(err) {
	// handle error here
}

See put() and get() for more details.

Caching

You can enable optional caching for JSON records, to store then in RAM for a given TTL, or up to a specific item count. Enable this feature by passing a cache object to the class constructor with additional settings. Example:

const S3 = require('s3-api');

let s3 = new S3({
	bucket: 'my-bucket',
	prefix: 'myapp/data/',
	cache: {
		maxAge: 3600
	}
});

This would cache all JSON files fetched using get(), and stored using put(), in memory for up to an hour (3600 seconds). You can also specify other limits including total cache keys, and limit to specific S3 keys by regular expression:

let s3 = new S3({
	bucket: 'my-bucket',
	prefix: 'myapp/data/',
	cache: {
		maxAge: 3600,
		maxItems: 1000,
		keyMatch: /^MYAPP\/MYDIR/
	}
});

This would limit the cache objects to 1 hour, and 1,000 total items (oldest keys will be expunged), and also only cache S3 keys that match the regular expression /^MYAPP\/MYDIR/.

Note that storing records via put() will always go to S3. This is a read cache, not a write cache. However, objects stored to S3 via put() may also be stored in the cache, if the key matches your ketMatch config property.

Remember that caching only happens for JSON records fetched using get(), and stored using put(). It does not happen for files, buffers or streams.

Using Files

The S3 API library provides wrappers for easily managing files in S3. Here is an example of uploading and downloading a file:

try {
	// upload file
	await s3.uploadFile({ localFile: '/path/to/image.gif', key: 's3dir/myfile.gif' });
	
	// download file
	await s3.downloadFile({ key: 's3dir/myfile.gif', localFile: '/path/to/image.gif' });
}
catch(err) {
	// handle error here
}

Streams are always used behind the scenes, so this can handle extremely large files without using significant memory. When downloading, the parent directories for the destination file will automatically be created if needed.

See uploadFile() and downloadFile() for more details.

Multiple Files

You can upload or download multiple files in one call, including entire directories, and traversal of nested directories. Here is how to do this:

try {
	// upload directory
	await s3.uploadFiles({ localPath: '/path/to/images/', remotePath: 's3dir/uploadedimages/' });
	
	// download directory
	await s3.downloadFiles({ remotePath: 's3dir/uploadedimages/', localPath: '/path/to/images/' });
}
catch(err) {
	// handle error here
}

This would upload the entire contents of the local /path/to/images/ directory, and place the contents into the S3 key s3dir/uploadedimages/ (i.e. using it as a prefix). Nested directories are automatically traversed as well. To control which files are uploaded or downloaded, use the filespec property:

try {
	// upload selected files
	await s3.uploadFiles({ localPath: '/path/to/images/', remotePath: 's3dir/uploadedimages/', filespec: /\.gif$/ });
	
	// download selected files
	await s3.downloadFiles({ remotePath: 's3dir/uploadedimages/', localPath: '/path/to/images/', filespec: /\.gif$/ });
}
catch(err) {
	// handle error here
}

This would only upload and download files with names ending in .gif. Note that the filespec only matches filenames, not directory paths. See uploadFiles() and downloadFiles() for more details.

Compression

The S3 API library can handle gzip compression and decompression for you by default. To do this, add compress for compression on upload, and decompress for decompression on download. Example use:

try {
	// upload file w/compression
	await s3.uploadFile({ localFile: '/path/to/report.txt', key: 's3dir/report.txt.gz', compress: true });
	
	// download file w/decompression
	await s3.downloadFile({ key: 's3dir/report.txt.gz', localFile: '/path/to/report.txt', decompress: true });
}
catch(err) {
	// handle error here
}

To control the gzip compression level and other settings, specify a gzip property in your class constructor:

let s3 = new S3({
	bucket: 'my-bucket',
	prefix: 'myapp/data/',
	gzip: {
		level: 6,
		memLevel: 8
	}
});

See the Node Zlib Class Options docs for more on these settings.

When compressing multiple files for upload, you can specify an S3 key suffix (to append .gz to all filenames for example):

try {
	// upload directory w/compression and suffix
	await s3.uploadFiles({ localPath: '/path/to/images/', remotePath: 's3dir/uploadedimages/', compress: true, suffix: '.gz' });
}
catch(err) {
	// handle error here
}

And similarly, when downloading with decompression you can use strip to strip off the .gz for the decompressed files:

try {
	// download directory w/decompression and strip
	await s3.downloadFiles({ remotePath: 's3dir/uploadedimages/', localPath: '/path/to/images/', decompress: true, strip: /\.gz$/ });
}
catch(err) {
	// handle error here
}

Threads

When uploading, downloading or deleting multiple files, you can specify a number of threads to use. This defaults to 1, meaning operate on a single file at a time, but S3 can often benefit from multiple threads in many cases, due to connection overhead and service lag. To increase the thread count, specify a threads property:

try {
	// upload directory
	await s3.uploadFiles({ localPath: '/path/to/images/', remotePath: 's3dir/uploadedimages/', threads: 4 });
	
	// download directory
	await s3.downloadFiles({ remotePath: 's3dir/uploadedimages/', localPath: '/path/to/images/', threads: 4 });
}
catch(err) {
	// handle error here
}

However, please be careful when using multiple threads with compression. All gzip operations run on the local CPU, not in S3, so you can easily overwhelm a server this way. It is recommended that you keep the threads at the default when using compression.

Pinging Objects

To "ping" an object is to quickly check for its existence and fetch basic information about it, without downloading the full contents. This is typically called "head" in HTTP parlance (i.e. "HTTP HEAD"), and thus the S3 API call is named head(). Example:

try {
	// ping a remote object
	let { meta } = await s3.head({ key: 's3dir/myfile.gif' });
	console.log(meta);
}
catch (err) {
	// handle error here
}

The meta object returned will have the object's size in bytes (size), and it's modification date as an Epoch timestamp (mtime). If the object does not exist, an error will be thrown.

Listing Objects

To generate a listing of remote objects on S3 under a specific key prefix, use the list() method:

try {
	// list remote objects
	let { files, bytes } = await s3.list({ remotePath: 's3dir/' });
	console.log(files);
}
catch (err) {
	// handle error here
}

This will list all the objects on S3 with a starting key prefix of s3dir, returning the array of files and total bytes used. The list() call traverses nested "directories" on S3, and also automatically manages "paging" through the results, so it returns them all in one single array (S3 only allows 1,000 objects per call, hence the need for pagination).

The files array will contain an object for each object found, with key, size and mtime properties. See list() below for more details.

To limit which objects are included in the listing, you can specify a filespec property:

try {
	// list remote gif files
	let { files, bytes } = await s3.list({ remotePath: 's3dir/', filespec: /\.gif$/ });
	console.log(files);
}
catch (err) {
	// handle error here
}

This would only include S3 keys that end with .gif.

For even finer grain control over which files are returned, you can specify a filter function, which will be invoked for each file. It will be passed a single object containing the key, size and mtime properties. The function can return true to include the file or false to exclude. Example use:

try {
	// list files larger than 1 MB
	let { files, bytes } = await s3.list({ 
		remotePath: 's3dir/', 
		filter: function(file) { return file.size > 1048576; } 
	});
	console.log(files);
}
catch (err) {
	// handle error here
}

Deleting Objects

To delete an object from S3, simply call delete() and specify the S3 key. Example:

try {
	// delete a remote object
	await s3.delete({ key: 's3dir/myfile.gif' });
}
catch (err) {
	// handle error here
}

To delete multiple objects in one call, use the deleteFiles() method. You can then set remotePath to specify a starting path, and optionally filespec to limit which files are deleted. Example:

try {
	// delete remote gif files
	await s3.deleteFiles({ remotePath: 's3dir/', filespec: /\.gif$/ });
}
catch (err) {
	// handle error here
}

Please note that deleteFiles() will recursively scan nested "directories" on S3, so use with extreme care.

Using Buffers

If you would rather deal with buffers instead of files, the S3 API library supports low-level putBuffer() and getBuffer() calls. This is useful if you already have a file's contents loaded into memory. Example:

let buf = fs.readFileSync( '/path/to/image.gif' );

try {
	// upload buffer
	await s3.putBuffer({ key: 's3dir/myfile.gif', value: buf });
	
	// download buffer
	let { data } = await s3.getBuffer({ key: 's3dir/myfile.gif' });
}
catch (err) {
	// handle error here
}

Remember, buffers are all held in memory, so beware of large objects that could melt your server. It is recommended that you use streams whenever possible (see next section).

Using Streams

Using streams is the preferred way of dealing with large objects, as they use very little memory. The API library provides putStream() and getStream() calls for your convenience. Here is an example of uploading a stream:

let readStream = fs.createReadStream( '/path/to/image.gif' );

try {
	// upload stream to S3
	await s3.putStream({ key: 's3dir/myfile.gif', value: readStream });
}
catch (err) {
	// handle error here
}

And here is an example of downloading a stream, and piping it to a file:

let writeStream = fs.createWriteStream( '/path/to/image.gif' );

try {
	// download stream from S3
	let { data } = await s3.getStream({ key: 's3dir/myfile.gif' });
	
	// pipe it to local file
	data.pipe( writeStream );
	
	writeStream.on('finish', function() {
		// download complete
	});
}
catch (err) {
	// handle error here
}

Note that putStream() will completely upload the entire stream to completion before returning, whereas getStream() simply starts a stream, and returns a handle to you for piping or reading.

Both stream methods can automatically compress or decompress with gzip if desired. Simply include a compress property and set it to true for upload compression, or a decompress property set to true for download decompression.

Custom S3 Params

All of the upload related calls (i.e. put(), update(), uploadFile(), uploadFiles(), putBuffer() and putStream()) accept an optional params object. This allows you specify options that are passed directly to the AWS S3 API, for things like ACL and Storage Class. Example:

let opts = {
	localFile: '/path/to/image.gif', 
	key: 's3dir/myfile.gif',
	params: {
		ACL: 'public-read',
		StorageClass: 'STANDARD_IA'
	}
};

try {
	// upload file
	await s3.uploadFile(opts);
}
catch(err) {
	// handle error here
}

This would set the ACL to public-read (see AWS - Canned ACL), and the S3 storage class to "Infrequently Accessed" (a cheaper storage tier with reduced redundancy and performance -- see AWS - Storage Classes). As of this writing, the supported storage class names are:

  • STANDARD
  • REDUCED_REDUNDANCY
  • STANDARD_IA
  • ONEZONE_IA
  • INTELLIGENT_TIERING
  • GLACIER
  • DEEP_ARCHIVE
  • GLACIER_IR

If you are uploading files to a S3 bucket that is hosting a static website, then you can use params to bake in headers like Content-Type and Cache-Control. Example:

let opts = {
	localFile: '/path/to/image.gif', 
	key: 's3dir/myfile.gif',
	params: {
		ContentType: 'image/gif',
		CacheControl: 'max-age=86400'
	}
};

try {
	// upload file
	await s3.uploadFile(opts);
}
catch(err) {
	// handle error here
}

You can alternatively declare some params in the class constructor, so you don't have to specify them for each API call:

let s3 = new S3({
	bucket: 'my-bucket',
	prefix: 'myapp/data/',
	params: {
		ACL: 'public-read',
		StorageClass: 'STANDARD_IA'
	}
});

try {
	// upload file
	await s3.uploadFile({ localFile: '/path/to/image.gif', key: 's3dir/myfile.gif' });
}
catch(err) {
	// handle error here
}

When params are specified in both places, they are merged together, and the properties in the API call take precedence over those defined in the class instance.

For a complete list of all the properties you can specify in params, see the AWS - PutObjectRequest docs.

Non-AWS S3 Providers

It is possible to connect to a non-AWS S3-compatible provider such as MinIO. To do this, you need to specify some additional properties when constructing the class:

let s3 = new S3({
	endpoint: "http://MINIO_HOST:9000",
	forcePathStyle: true,
	
	credentials: {
		accessKeyId: "YOUR_ACCESS_KEY_HERE",
		secretAccessKey: "YOUR_SECRET_KEY_HERE"
	},
	
	bucket: 'my-bucket',
	prefix: 'myapp/data/'
});

See the class constructor reference for more details.

To start a local MinIO server using Docker, run this command:

docker run --name minio -p 9000:9000 -p 9001:9001 quay.io/minio/minio server /data --console-address ":9001"

Logging

You can optionally attach a pixl-logger compatible logger to the API class, which can log all requests and responses, as well as errors. Example:

const Logger = require('pixl-logger');
let logger = new Logger( 'debug.log', ['hires_epoch', 'date', 'hostname', 'component', 'category', 'code', 'msg', 'data'] );

s3.attachLogAgent( logger );

Debug log entries are logged at levels 8 and 9, with the component column set to S3. Errors are logged with the component set to S3 and the code column set to one of the following:

Error Code Description
err_s3_get An S3 core error attempting to fetch an object. Note that a non-existent object is not logged as an error.
err_s3_put An S3 core error attempting to put an object.
err_s3_delete An S3 core error attempting to delete an object. Note that a non-existent object is not logged as an error.
err_s3_head An S3 core error attempting to head (ping) an object. Note that a non-existent object is not logged as an error.
err_s3_json A JSON parser error when fetching a JSON record.
err_s3_file A local filesystem error attempting to stat a file.
err_s3_dir A local filesystem error attempting to create directories.
err_s3_glob A local filesystem error attempting to glob (scan) files.
err_s3_stream A read or write stream error.
err_s3_gzip An error attempting to compress or decompress via gzip (zlib).

In all cases, a verbose error description will be provided in the msg column.

Console

To log everything to the console, you can simulate a pixl-logger compatible logger like this:

s3.attachLogAgent( {
	debug: function(level, msg, data) {
		console.log( code, msg, data );
	},
	error: function(code, msg, data) {
		console.error( code, msg, data );
	}
} );

Performance Tracking

You can optionally attach a pixl-perf compatible performance tracker to the API class, which will measure all S3 calls for you. Example:

const Perf = require('pixl-perf');
let perf = new Perf();
perf.begin();

s3.attachPerfAgent( perf );

It will track the following performance metrics for you:

Perf Metric Description
s3_put Measures all S3 upload operations, including put(), uploadFile(), uploadFiles(), putBuffer() and putStream()).
s3_get Measures all S3 download operations, including get(), downloadFile(), downloadFiles(), getBuffer() and getStream()).
s3_head Measures all calls to head().
s3_list Measures all calls to list().
s3_copy Measures all calls to copyFile().
s3_delete Measures all calls to deleteFile() and deleteFiles().

Unit Tests

To run the unit tests, you must set some environment variables instructing the code which S3 bucket and region to use. Example:

S3API_TEST_REGION=us-west-1 S3API_TEST_BUCKET=my-bucket npm test

All test records will be created under a test/s3apiunit/PID/ key prefix, and everything will be deleted when the tests complete. If any tests fail, however, there may be a few records leftover (deliberately, for debugging purposes), so you may need to delete those manually.

If you do not have automatic AWS authentication setup on your machine (e.g. ~/.aws/credentials file or other), you may need to set the following two environment variables as well:

S3API_TEST_ACCESSKEYID
S3API_TEST_SECRETACCESSKEY

CLI Usage

s3-api comes with a CLI tool which you can use to send S3 API calls from your terminal. When you install the module globally (see below), it installs a single command called s3 which is the CLI entry point. The general syntax of the CLI is:

s3 COMMAND [ARG1 ARG2...] [--KEY VALUE --KEY VALUE...]

Example command:

s3 copy /path/to/image.gif s3://my-bucket/s3dir/myfile.gif

Each command typically takes one or more plain arguments, and most also support a number of "switches" (key/value arguments specified using a double-dash, e.g. --key value).

Please note that the standard AWS S3 CLI is a much more feature-rich (not to mentioned battle-hardened) tool, and you should consider using that instead. This module is a simplified wrapper that only supports basic S3 commands.

Installation

Use npm to install the module globally like this:

npm install -g s3-api

This will install a global s3 command in your PATH (typically in /usr/bin).

File Management

The CLI allows you to easily upload and download files to/from S3 using the uploadFile and downloadFile commands. However, it's easier to just use the omni copy command which does everything. Here are examples:

# Upload file to S3
s3 copy /path/to/image.gif s3://my-bucket/s3dir/myfile.gif

# Download file from S3
s3 copy s3://my-bucket/s3dir/myfile.gif /path/to/image.gif

You can also upload and download multiple files and entire directories using uploadFiles and downloadFiles, or just add --recursive to the copy command, which is easier to remember:

# Upload entire folder to S3
s3 copy /path/to/images/ s3://my-bucket/s3dir/uploaded/ --recursive

# Download entire folder from S3
s3 copy s3://my-bucket/s3dir/uploaded/ /path/to/images/ --recursive

These recursive commands provide several ways of filtering files and paths to exclude files, or only include certain files. Example:

# Only upload GIF images
s3 copy /path/to/images/ s3://my-bucket/s3dir/uploaded/ --recursive --filespec '\.gif$'

# Only download files over than 1 week
s3 copy s3://my-bucket/s3dir/uploaded/ /path/to/images/ --recursive --older "1 week"

# Only upload files larger than 2MB
s3 copy /path/to/images/ s3://my-bucket/s3dir/uploaded/ --recursive --larger "2 MB"

The upload and download commands all support optional upload compression, and/or download decompression, so you can store .gz compressed files in S3, and decompress them on download. Examples:

# Upload a bunch of files and compress with gzip (and add ".gz" suffix to all S3 files)
s3 copy /path/to/files/ s3://my-bucket/s3dir/uploaded/ --recursive --compress --suffix ".gz"

# Download a bunch of gzip files and decompress (and strip off ".gz" suffix)
s3 copy s3://my-bucket/s3dir/uploaded/ /path/to/files/ --recursive --decompress --strip '\.gz$'

You can also copy & move files between two S3 locations (across buckets too, if you want) using copy and move:

# Copy file from S3 to S3
s3 copy s3://my-bucket/users/oldkermit.json s3://my-bucket/users/newkermit.json

# Move file from S3 to S3
s3 move s3://my-bucket/users/oldkermit.json s3://my-bucket/users/newkermit.json

And to copy or move multiple files and folders from S3 to S3, use copyFiles or moveFiles, or simply add --recursive:

# Copy entire S3 folder
s3 copy s3://my-bucket/users/ s3://my-bucket/newusers/ --recursive

# Move entire S3 folder
s3 move s3://my-bucket/users/ s3://my-bucket/newusers/ --recursive

You can delete single S3 files and entire folder trees using the delete and deleteFiles commands (or just add --recursive to delete):

# Delete S3 file
s3 delete s3://my-bucket/users/newkermit.json

# Delete entire S3 folder
s3 delete s3://my-bucket/s3dir/uploaded/ --recursive

The recursive deleteFiles command also accepts all the filtering options that uploadFiles and downloadFiles use. Example:

# Delete selected S3 files
s3 delete s3://my-bucket/s3dir/uploaded/ --recursive --filespec '\.gif$' --older "15 days"

To check if a S3 file exists and view its metadata, use the head command:

s3 head s3://my-bucket/s3dir/myfile.gif

Raw Streams

You can upload and download raw streams from STDIN, or to STDOUT, using the putStream and getStream commands. Examples:

# Upload stream from file
cat /path/to/myfile.gif | s3 putStream s3://my-bucket/s3dir/myfile.gif

# Download stream to file
s3 getStream s3://my-bucket/s3dir/myfile.gif --quiet > /path/to/myfile.gif

Listing

To list remote files in S3, including files in nested folders, use the list command:

s3 list s3://my-bucket/s3dir/

To list only a single level of files and folders, use the listFolders command:

s3 listFolders s3://my-bucket/s3dir/

To list all your S3 buckets, use the listBuckets command:

s3 listBuckets

These three commands all support JSON (--json) and CSV (--csv) output, as well as human-readable ASCII tables (the default).

Key Value JSON

If you want to use S3 as a key/value store, then this is the CLI for you. The put and get commands store and fetch objects, serialized to/from JSON behind the scenes. Examples:

# Put JSON record using raw JSON
s3 put s3://my-bucket/users/kermit.json '{"animal":"frog", "color":"green"}'

# Build JSON record using dot.path.notation
s3 put s3://my-bucket/users/kermit.json --value.animal "frog" --value.color "green"

# Get JSON record and pretty-print to console
s3 get s3://my-bucket/users/kermit.json --pretty

You can also use the update command to make edits to JSON records using dot.path.notation. Example:

s3 update s3://my-bucket/users/kermit.json --update.animal "toad" --update.color "yellow"

Using dot.path.notation you can add, replace and delete keys, access nested keys inside of objects, and even create new objects. See the reference guide for details.

Backups

The backup command makes a point-in-time backup of a local directory, compresses it using .zip, .tar, .tar.gz, .tar.xz or .tar.bz2, and uploads the archive to S3. Example:

s3 backup /path/to/files/ s3://my-bucket/backups/mybackup-[yyyy]-[mm]-[dd].zip

You can use date/time placeholders in the destination S3 key, to embed a custom timestamp. You can even put them in folder names, e.g. s3://my-bucket/backups/[yyyy]/[mm]/mybackup-[yyyy]-[mm]-[dd].zip.

If you make backups on a schedule, and only want to keep a certain amount in S3, add an --expire argument with a relative time (e.g. 30 days) and the backup command will automatically delete archives that fall outside the specified date range.

You can also restore backups using the restoreBackup command. This reverses the process, downloads a backup archive from S3, and decompresses it back onto the filesystem. Example:

s3 restoreBackup s3://my-bucket/backups/mybackup-2024-05-22.zip /path/to/files/

You can also optionally "pre-delete" the local directory to ensure an exact restoration. To do this, add a --delete argument to the command. Example:

s3 restoreBackup s3://my-bucket/backups/mybackup-2024-05-22.zip /path/to/files/ --delete

Snapshots

A "snapshot" works in the opposite direction of a backup. A snapshot is a effectively a point-in-time backup of an S3 location, including all nested files and directories. The snapshot command downloads all S3 files and writes a local .zip, .tar, .tar.gz, .tar.xz or .tar.bz2 archive file. Example:

s3 snapshot s3://my-bucket/s3dir/images/ /path/to/snapshot-[yyyy]-[mm]-[dd].zip

This would download and compress the entire s3://my-bucket/s3dir/images/ location and everything under it, and write it to /path/to/snapshot-[yyyy]-[mm]-[dd].zip on local disk. You can use date/time placeholders in the destination filename and/or folder names.

If you take snapshots on a schedule, and only want to keep a certain amount on disk, add an --expire argument with a relative time (e.g. 30 days) and the snapshot command will automatically delete snapshots that fall outside the specified date range.

To restore a snapshot back to S3, use the restoreSnapshot command. This decompresses a snapshot archive and re-uploads all files back to their original location (or a custom location). Example:

s3 restoreSnapshot /path/to/snapshot-2024-05-22.zip s3://my-bucket/s3dir/images/

You can also optionally "pre-delete" the target S3 location to ensure an exact restoration. To do this, add a --delete argument to the command. Example:

s3 restoreSnapshot /path/to/snapshot-2024-05-22.zip s3://my-bucket/s3dir/images/ --delete

Config File

The CLI supports an optional configuration file, which should live in your home directory and be named .s3-config.json (with a leading period). Example file path for root:

/root/.s3-config.json

The file should be in JSON format, and can store one or more Common Arguments that act as defaults for the CLI. This is a great way to specify your AWS region, logging options, and other things as well, so you don't have to pass them on the command-line every time. Example config file:

{
	"region": "us-west-1",
	"log": "/var/log/s3-cli.log"
}

You can also use dot.path.notation in this file to configure things such as AWS credentials, a default S3 storage class, and/or default ACL for all S3 objects. Example:

{
	"region": "us-west-1",
	"log": "/var/log/s3-cli.log",
	
	"credentials.accessKeyId": "YOUR_ACCESS_KEY_HERE",
	"credentials.secretAccessKey": "YOUR_SECRET_KEY_HERE",
	
	"params.StorageClass": "STANDARD_IA",
	"params.ACL": "public-read"
}

Note that arguments in the config file should not have a double-dash prefix like they do on the command-line.

CLI Logging

The CLI supports an optiona log file, which contains all output, as well as verbose debug information. To enable the log, add --log FILE to any command, or place it in your config file. Each line is annotated with a timestamp. Example log snippet:

[2024/05/26 14:03:16] 🪣 S3 API v2.0.0
[2024/05/26 14:03:16] {"region":"us-west-1","bucket":"my-bucket","key":"users-test.json","updates":{"num":1}}
[2024/05/26 14:03:16] Updating JSON record: users-test.json
[2024/05/26 14:03:16] {"bucket":"my-bucket","key":"users-test.json","updates":{"num":1}}
[2024/05/26 14:03:16] Fetching JSON record: users-test.json
[2024/05/26 14:03:16] {"bucket":"my-bucket","key":"users-test.json"}
[2024/05/26 14:03:16] Fetching stream: users-test.json
[2024/05/26 14:03:16] {"Metadata":{"animal":"frog","num":1},"Bucket":"my-bucket","Key":"users-test.json"}
[2024/05/26 14:03:16] Stream started: users-test.json
[2024/05/26 14:03:16] Converting stream to buffer: users-test.json
[2024/05/26 14:03:16] Fetch complete: users-test.json
[2024/05/26 14:03:16] 21 bytes
[2024/05/26 14:03:16] JSON fetch complete: users-test.json
[2024/05/26 14:03:16] Storing JSON record: users-test.json
[2024/05/26 14:03:16] {"bucket":"my-bucket","key":"users-test.json"}
[2024/05/26 14:03:16] Storing Buffer: users-test.json (21 bytes)
[2024/05/26 14:03:16] {"ContentType":"application/json"}
[2024/05/26 14:03:16] Storing Stream: users-test.json
[2024/05/26 14:03:16] {"Metadata":{"animal":"frog","num":"1"},"ContentType":"application/json","Bucket":"my-bucket","Key":"users-test.json"}
[2024/05/26 14:03:16] Store complete: users-test.json

A few other notes about the CLI log:

  • Verbose debugging information is always logged, even if --verbose mode is disabled.
  • The log will contain all CLI output even if it is silenced with --quiet mode.
  • All color is stripped from the log.

CLI Reference

See the CLI Reference for more details.

License

The MIT License (MIT)

Copyright (c) 2023 - 2024 Joseph Huckaby and PixlCore.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/s3-api/

    Package Sidebar

    Install

    npm i s3-api

    Weekly Downloads

    606

    Version

    2.0.11

    License

    MIT

    Unpacked Size

    270 kB

    Total Files

    10

    Last publish

    Collaborators

    • jhuckaby