~15KB lightweight S3 client with zero dependencies, designed for Node.js, edge computing like Cloudflare workers, AWS Lambda (and browsers - not implemented yet).
import { S3 } from 'ultralight-s3';
// ... your configuration
const s3 = new S3({
accessKeyId: 'your-access-key-id',
secretAccessKey: 'your-secret-access-key',
region: 'auto',
bucket: 'your-bucket-name',
});
// List objects
const objects = await s3.list();
// or with prefix
// const specificObjectsUnderPrefix = await s3.list('/', 'prefix');
console.log(objects);
// Check if a file exists
const exists = await s3.fileExists('path/to/file.txt');
// Get a life
const data = await s3.get('path/to/life.txt');
console.log(data);
// get a stream of a large file (first chunk)
const firstChunk = await s3.getResponse('path/to/large-file.mp4', false, 0).body;
// get a stream of a large file (all chunks)
const allChunks = await s3.getResponse('path/to/large-file.mp4', true);
for await (const chunk of allChunks.body) {
console.log(chunk);
}
// Upload a large file
// by default is 5MB per request (minimum for AWS S3 is 5MB)
const chunkSize = s3.getMaxRequestSizeInBytes();
// Initiate multipart upload
const uploadId = await s3.getMultipartUploadId('randomFileName.fastaq', 'text/plain');
const buffer = randomBytes(chunkSize * 2 + 1024); // Just over 2 parts
const upload = await s3.uploadPart('randomFileName.fastaq', buffer, uploadId, 1);
const upload2 = await s3.uploadPart('randomFileName.fastaq', buffer, uploadId, 2);
const upload3 = await s3.uploadPart('randomFileName.fastaq', buffer, uploadId, 3);
// Complete multipart upload
const result = await s3.completeMultipartUpload('randomFileName.fastaq', uploadId, [
{ partNumber: 1, ETag: upload.ETag },
{ partNumber: 2, ETag: upload2.ETag },
{ partNumber: 3, ETag: upload3.ETag },
]);
console.log(result);
// Get file size
const size = await s3.getContentLength('path/to/file.txt');
console.log(size);
// Put a file
await s3.put('path/to/file.txt', Buffer.from('Hello, World!'));
// Delete a file
await s3.delete('path/to/file.txt');
For some examples, check the dev directory and try to use it with Hono or Cloudflare Workers.
npm install ultralight-s3
# or
yarn add ultralight-s3
# or
pnpm add ultralight-s3
# or
# Not yet implemented
# <script src="https://unpkg.com/ultralight-s3/dist/ultralight-s3.min.js" defer></script>
import { S3 } from 'ultralight-s3';
const s3 = new S3({
accessKeyId: 'your-access-key-id',
secretAccessKey: 'your-secret-access-key',
endpoint: 'https://your-s3-endpoint.com' || 'http://127.0.0.1:9000',
bucketName: 'your-bucket-name',
region: 'auto', //optional - by default is auto
maxRequestSizeInBytes: 5242880, // optional - by default is 5MB
requestAbortTimeout: undefined, // optional - for aborting requests
logger: console, // optional - for debugging
});
import { S3 } from 'ultralight-s3';
const s3 = new S3({
accessKeyId: 'your-access-key-id',
secretAccessKey: 'your-secret-access-key',
endpoint: 'https://your-clouflare-id.r2.cloudflarestorage.com/your-bucket-name',
bucketName: 'your-bucket-name',
region: 'auto', //optional - by default is auto
maxRequestSizeInBytes: 5242880, // optional - by default is 5MB
requestAbortTimeout: undefined, // optional - for aborting requests
logger: console, // optional - for debugging
});
Not tested, but should work with other S3 compatible services. Full list - soon to come. PRs are welcome.
bucketExists()
: Check if a bucket exists.get(key, opts)
: Get an object from the bucketgetObjectWithETag(key, opts)
: Get an object and its ETag from the bucketgetEtag(key, opts)
: Get the ETag of an objectgetResponse(key, wholeFile, rangeFrom, rangeTo, opts)
: Get a response of an object from the bucketput(key, data)
: Put an object into the bucketdelete(key)
: Delete an object from the bucketgetContentLength(key)
: Get the content length of an objectfileExists(key)
: Check if an object exists in the bucketlist(delimiter, prefix, maxKeys, method, opts)
: List objects in the bucketlistMultiPartUploads(delimiter, prefix, method, opts)
: List multipart uploads in the bucketgetMultipartUploadId(key, fileType)
: Initiate a multipart uploaduploadPart(key, data, uploadId, partNumber, opts)
: Upload a part in a multipart uploadcompleteMultipartUpload(key, uploadId, parts)
: Complete a multipart uploadabortMultipartUpload(key, uploadId)
: Abort a multipart uploadgetBucketName()
: Get the current bucket namesetBucketName(bucketName)
: Set the bucket namegetRegion()
: Get the current regionsetRegion(region)
: Set the regiongetEndpoint()
: Get the current endpointsetEndpoint(endpoint)
: Set the endpointgetMaxRequestSizeInBytes()
: Get the maximum request size in bytessetMaxRequestSizeInBytes(maxRequestSizeInBytes)
: Set the maximum request size in bytessanitizeETag(etag)
: Sanitize an ETag stringgetProps()
: Get all configuration propertiessetProps(props)
: Set all configuration propertiesnew S3(config: Object)
accessKeyId: string
: The access key ID for authentication.secretAccessKey: string
: The secret access key for authentication.endpoint: string
: The endpoint URL of the S3-compatible service.bucketName: string
: The name of the bucket to operate on.region?: string
(optional): The region of the S3 service (default: 'auto').maxRequestSizeInBytes?: number
(optional): The maximum size of a single request in bytes (minimum 5MB).requestAbortTimeout?: number
(optional): The timeout in milliseconds after which a request should be aborted.logger?: Object
(optional): A logger object with methods like info, warn, error.list(delimiter?: string, prefix?: string, maxKeys?: number, method?: string, opts?: Object): Promise<Array<Object>
delimiter?: string
(optional): The delimiter to use for grouping objects in specific path (default: '/').prefix?: string
(optional): The prefix to filter objects in specific path (default: '').maxKeys?: number
(optional): The maximum number of keys to return (default: 1000).method?: string
(optional): The HTTP method to use (default: 'GET').opts?: Object
(optional): Additional options for the list operation.put(key: string, data: Buffer | string): Promise<Object>
key: string
: The key of the object to put.data: Buffer | string
: The content of the object.
get(key: string, opts?: Object): Promise
key: string
: The key of the object to get.opts?: Object
(optional): Additional options for the get operation.getObjectWithETag(key: string, opts?: Object): Promise<{ etag: string | null; data: string | null }>
key: string
: The key of the object to get.opts?: Object
(optional): Additional options for the get operation.getResponse(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Object): Promise<Response>
key: string
: The key of the object to get.wholeFile?: boolean
(optional): Whether to get the whole file or a part (default: true).rangeFrom?: number
(optional): The byte range from to get if not getting the whole file (default: 0).rangeTo?: number
(optional): The byte range to to get if not getting the whole file (default: maxRequestSizeInBytes). Note: rangeTo is inclusive.opts?: Object
(optional): Additional options for the get operation.getEtag(key: string, opts?: Object): Promise<string | null>
key: string
: The key of the object to get the ETag for.opts?: Object
(optional): Additional options for the operation.key: string
: The key of the object to delete.
fileExists(key: string): Promise
key: string
: The key of the object to check.getContentLength(key: string): Promise<number>
key: string
: The key of the object.listMultiPartUploads(delimiter?: string, prefix?: string, method?: string, opts?: Object): Promise<Array<Object>
delimiter?: string
(optional): The delimiter to use for grouping objects in specific path (default: '/').prefix?: string
(optional): The prefix to filter objects in specific path (default: '').method?: string
(optional): The HTTP method to use (default: 'GET').opts?: Object
(optional): Additional options for the list operation.
getMultipartUploadId(key: string, fileType?: string): Promise
key: string
: The key of the object to upload.fileType?: string
(optional): The MIME type of the file (default: 'application/octet-stream').uploadPart(key: string, data: Buffer | string, uploadId: string, partNumber: number, opts?: Object): Promise<{ partNumber: number, ETag: string }>
key: string
: The key of the object being uploaded.data: Buffer | string
: The content of the part.uploadId: string
: The upload ID of the multipart upload.partNumber: number
: The part number.opts?: Object
(optional): Additional options for the upload.completeMultipartUpload(key: string, uploadId: string, parts: Array<{ partNumber: number, ETag: string }>): Promise<Object>
key: string
: The key of the object being uploaded.uploadId: string
: The upload ID of the multipart upload.parts: Array<{ partNumber: number, ETag: string }>
: An array of objects containing PartNumber and ETag for each part.abortMultipartUpload(key: string, uploadId: string): Promise<Object>
key: string
: The key of the object being uploaded.uploadId: string
: The ID of the multipart upload to abort.bucketExists(): Promise<boolean>
sanitizeETag(etag: string): string
etag: string
: The ETag to sanitize.Also all essential getters and setters for the config object.
Stay connected with the community and get support.
Contributions are welcome! Please open an issue or submit a pull request.
This project is licensed under the MIT License - see the LICENSE.md file for details.