Skip to content
Files SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Amazon S3

AWS S3 (and any S3-compatible bucket). Uses the standard AWS credential chain - environment, IAM role, shared profile.

Installation

@aws-sdk/client-s3, @aws-sdk/s3-presigned-post, and @aws-sdk/s3-request-presigner are optional peer dependencies of files-sdk - install alongside the SDK so the adapter’s imports resolve at runtime.

npm install files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner
pnpm add files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner
yarn add files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner
bun add files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner
nub add files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner
aube add files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner

@aws-sdk/lib-storage is optional too. Install it to report true byte-level upload progress via onProgress, to use multipart, or to upload a ReadableStream body of unknown length. It’s only loaded when upload uses onProgress, multipart, or a ReadableStream body of unknown length.

On Cloudflare Workers and other edge runtimes without a DOMParser, @aws-sdk/client-s3’s XML parsing fails at runtime. Use s3Fetch() from files-sdk/s3-fetch there instead — the same S3 protocol over a SigV4-signed fetch, with no @aws-sdk/* packages.

Usage

import { Files } from "files-sdk";
import { s3 } from "files-sdk/s3";

const files = new Files({
  adapter: s3({
    bucket: "uploads",
    region: "us-east-1",
    // credentials auto-loaded from the AWS chain
    // (env vars, IAM role, shared profile, ...)
  }),
});

Canonical AWS S3 buckets support provider-native conditional create, replace, exact read, delete, and copy. Conditional copy sends its source ETag and destination create/replace predicate in one CopyObject request.

The primitives are exposed only when the client will talk to AWS: no endpoint option and no AWS_ENDPOINT_URL_S3 / AWS_ENDPOINT_URL redirect in the environment (the AWS SDK honors those on its own), or one whose hostname is under amazonaws.com (or amazonaws.com.cn), such as an explicit regional, VPC, FIPS, or dual-stack endpoint. The same check decides whether the adapter declares S3 event records for files-sdk/events (capabilities.events). A shared-config endpoint_url — profile-level or under a services section — is invisible at construction, so it is caught at request time instead: a conditional request whose resolved hostname is not under amazonaws.com (or amazonaws.com.cn in the China regions) fails closed before it is sent. S3-compatible services differ in conditional-header support, so in every one of those cases the adapter refuses rather than risk an unconditional overwrite. AWS-hosted endpoints (VPC, FIPS, dual-stack, GovCloud) resolve under amazonaws.com and need no override. Pass conditional: true to opt an S3-compatible endpoint you have verified back in (it skips both checks), or conditional: false to disable the primitives on a canonical bucket.

Conditional copy needs @aws-sdk/client-s3 3.919.0 or newer, where CopyObject gained IfMatch / IfNoneMatch (the peer range now starts at 3.1079.0). Because an optional peer range is advisory, the adapter also verifies at request time that every predicate it set was actually serialized as a header and rejects the call if the installed client dropped one. Conditional uploads take a buffered body (Blob, Uint8Array, ArrayBuffer, or string) — a stream is rejected before I/O.

Options

PropType
bucketstring

S3 bucket name. The adapter scopes all operations to it.

Typestring
region?string

AWS region the bucket lives in (e.g. `us-east-1`). Falls back to `AWS_REGION`, then `AWS_DEFAULT_REGION`; required if neither is set.

Typestring
endpoint?string

Override the S3 service endpoint. Use this to point at S3-compatible services (DigitalOcean Spaces, Wasabi, Backblaze B2, LocalStack, etc.). With an explicit endpoint the client sends request checksums, and validates response checksums, only when the operation requires them (`requestChecksumCalculation` / `responseChecksumValidation` set to `"WHEN_REQUIRED"`), because several S3-compatible services reject the `x-amz-checksum-crc32` header newer SDKs add by default. Set `AWS_REQUEST_CHECKSUM_CALCULATION` / `AWS_RESPONSE_CHECKSUM_VALIDATION` to override.

Typestring
forcePathStyle?boolean

Use path-style addressing (`https://endpoint/bucket/key`) instead of virtual-hosted style (`https://bucket.endpoint/key`). Required by some S3-compatible services and by LocalStack.

Typeboolean
conditional?boolean

Whether to expose the native conditional primitives (`If-Match` / `If-None-Match` create, replace, exact read, delete, and copy). Defaults to `true` only when the client will talk to AWS S3: no `endpoint` here and no `AWS_ENDPOINT_URL_S3` / `AWS_ENDPOINT_URL` redirect in the environment, or one whose hostname is `amazonaws.com` / `amazonaws.com.cn` (or a subdomain of either). S3-compatible services differ in which conditional headers they honor, so the adapter fails closed for them rather than risk an unconditional overwrite. The same test decides whether the adapter declares S3 event records (`capabilities.events`). A shared-config `endpoint_url` (profile- or service-level) is invisible at construction, so it is caught at request time instead: a conditional request whose resolved hostname is not `amazonaws.com` or `amazonaws.com.cn` (or a subdomain of either) fails closed before it is sent. AWS-hosted endpoints (VPC, FIPS, dual-stack, GovCloud, the China regions) all resolve under those suffixes and need no override. Set `true` to opt an S3-compatible endpoint that you have verified honors `If-Match` / `If-None-Match` in — this skips both the constructor check and the request-time hostname check — or `false` to disable the primitives on a canonical bucket.

Typeboolean
credentials?{ accessKeyId: string; secretAccessKey: string; sessionToken?: string; }

Static credentials. Skip to use the AWS credential chain (env vars, IAM role, shared profile, EC2/ECS/EKS instance metadata).

Type{ accessKeyId: string; secretAccessKey: string; sessionToken?: string; }
publicBaseUrl?string

Origin used to build URLs from `url()`. When set, `url(key)` returns `${publicBaseUrl}/${key}` and skips signing — appropriate for buckets fronted by a CDN, public-read policy, or custom domain. When unset, `url()` falls back to a presigned `GetObject` URL (see {@link defaultUrlExpiresIn}). An explicit `url(key, { expiresIn })` or `responseContentDisposition` still presigns even when this is set, since a permanent link can't expire or carry the override. A trailing slash on the base is tolerated. Each key segment is URL-encoded (the `/` separators are kept), so pass raw keys; a pre-encoded key would be double-encoded.

Typestring
defaultUrlExpiresIn?number

Default expiry, in seconds, for the presigned URLs returned by `url()` when no per-call `expiresIn` is given. Defaults to 3600 (1 hour). Per-call `url(key, { expiresIn })` overrides.

Typenumber
defaultProviderMessage?string

Override the fallback message used when an unknown error has no `message` of its own, and the label on the adapter's own errors. Internal — set by the S3-compatible wrappers (r2, minio, wasabi, …) so their users see "R2 error" instead of "S3 error".

Typestring

defaultProviderMessage is internal: the S3-compatible wrappers set it so their errors read “Wasabi error” rather than “S3 error”. You don’t need to pass it.

Limits

These come from S3 itself, so they apply to s3Fetch() and every S3-compatible wrapper as well:

  • Presigned URLs live at most 7 days. SigV4 caps a presigned URL at 604800 seconds, so url() and signedUploadUrl() (a presigned PUT, or with maxSize a presigned POST) throw an Invalid error for a longer expiresIn (or defaultUrlExpiresIn) instead of returning a URL the server would reject. The lifetime must also be a whole number of seconds, at least 1; a zero, negative, or fractional one throws Invalid too. capabilities.signedUrl.maxExpiresIn reports the ceiling. With a publicBaseUrl, a plain url(key) returns the permanent link, so the cap doesn’t apply to it; url(key, { expiresIn }) still presigns and is capped.
  • copy() and move() switch to a multipart copy past 5 GiB. A copy is one server-side CopyObject request, which S3 limits to 5 GiB sources. When S3 refuses a larger source, the adapter (on both engines) copies it server-side with UploadPartCopy in 512 MiB parts instead, carrying the source’s Content-Type, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Expires, and user metadata. Object tags aren’t carried. On AWS every part is pinned to the source’s ETag, so a source overwritten mid-copy fails with Conflict. A conditional copy stays a single request, so an oversized source throws Unsupported.
  • User metadata travels as x-amz-meta-* headers. Keys must be HTTP tokens (letters, digits, and the punctuation HTTP allows in a header name), and values printable ASCII without control characters. Latin-1 text such as café is refused too: the HTTP client sends é as one byte while SigV4 signs it as two, so S3 would reject the request as a signature mismatch. Anything else throws Invalid before any request is sent; encode such values first (for example with encodeURIComponent). The same rule applies to contentType and cacheControl, which travel as the Content-Type and Cache-Control headers.
  • Multipart and resumable (control) uploads fit S3’s part limits. Every part but the last must be at least 5 MiB, and an upload holds at most 10,000 parts of at most 5 GiB each. The adapter raises a smaller multipart.partSize to 5 MiB and, when the body’s length is known, raises it further until the body fits in 10,000 parts. An unknown-length stream can’t be fitted, so give it a partSize big enough on its own. A resumable upload pins the chosen size in its resume token.

S3-compatible endpoints and checksums

Recent @aws-sdk/client-s3 versions add an x-amz-checksum-crc32 header to every PutObject and UploadPart, and some S3-compatible services reject it. When you pass an endpoint, the adapter sets requestChecksumCalculation and responseChecksumValidation to "WHEN_REQUIRED", so checksums are only sent and checked where an operation requires one. DeleteObjects still carries one, because S3 requires it. Set AWS_REQUEST_CHECKSUM_CALCULATION / AWS_RESPONSE_CHECKSUM_VALIDATION to override. Canonical AWS (no endpoint) keeps the SDK default. Presigned PUT URLs from signedUploadUrl() never carry a precomputed checksum, on any endpoint: the SDK would sign the empty body’s CRC32 into the URL, and S3 would reject every real upload through it.

Compatibility

Method Status Notes
upload ✅
download ✅
delete ✅
list ⚠️ The S3 list API returns no per-object Content-Type, so contentType is inferred from the key’s extension (application/octet-stream when unknown). Use head() for the stored value.
search ✅
head ✅
exists ✅
copy ✅ Server-side CopyObject, or a server-side multipart copy for sources over 5 GiB (see Limits).
url ✅ Presigned URLs are capped at 7 days (see Limits).
signedUploadUrl ✅ A positive minSize needs maxSize, which selects the presigned POST policy that enforces both; on its own it throws Unsupported.
conditional operations ✅ Single-object native requests. Bulk, multipart, resumable, and custom-endpoint conditionals are unsupported.

Last updated on

Was this page helpful?