@aws-blocks/bb-file-bucket

中文文档
0.4.0发布于 2026/11/02Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 6.5万

File storage backed by Amazon S3.

原始使用文档
文档目录22 节
  1. API
  2. Options
  3. PutOptions
  4. Presigned URL options
  5. CorsRule
  6. LifecycleRule
  7. FileVersionInfo
  8. Error Handling
  9. Examples
  10. Basic Put/Get
  11. Presigned Upload URL
  12. Browser Uploads and Downloads (CORS)
  13. Listing Files with Scan
  14. Batch Delete
  15. Wrapping an Existing Bucket
  16. Versioned Bucket
  17. Access Logging
  18. Encryption with KMS
  19. Object Lock
  20. Best Practices
  21. Scaling & Cost (AWS)
  22. Local Development

FileBucket

File storage backed by Amazon S3.

When to use: You need to store, retrieve, or serve binary files — user uploads, generated reports, images, videos, or static assets.

When NOT to use: If you need structured key-value data with conditional writes, use KVStore. If you need queryable records with indexes, use DistributedTable.

Design & mock parity details: DESIGN.md

API

const bucket = new FileBucket(scope, id, options?)
MethodReturnsDescription
put(path, body, options?)Promise<void>Upload a file. Overwrites any existing file at the path.
get(path, options?)Promise<FileContent | null>Download a file. Returns null if absent. Pass { versionId } on versioned buckets.
delete(path, options?)Promise<void>Delete a file. No-op if absent. Pass { versionId } on versioned buckets to permanently delete a version.
deleteBatch(paths)Promise<void>Delete multiple files. Chunks internally at 1,000 keys.
getUrl(path, options?)Promise<string>Generate a presigned download URL. Accepts versionId on versioned buckets.
putUrl(path, options?)Promise<string>Generate a presigned upload URL.
getFileHandle(path, options?)Promise<FileDownloadClient>Get a download handle for browser-side use. Accepts versionId on versioned buckets.
createUploadHandle(path, options?)Promise<FileUploadClient>Get an upload handle for browser-side use.
scan(options?)AsyncIterable<FileInfo>List files. Use prefix to scope.
listVersions(path)Promise<FileVersionInfo[]>List all versions of a file (versioned buckets only). Newest first.
restoreVersion(path, versionId)Promise<void>Restore a previous version as the current version.
FileBucket.fromExisting(bucketName)ExternalBucketRefWrap a pre-existing S3 bucket.

Options

OptionTypeDescription
versionedbooleanEnable S3 object versioning. Default: true. Pass versioned: false to opt out. Note: the version-aware method typings (versionId on get/delete/getUrl/getFileHandle) are selected only by the literal versioned: false; a non-literal boolean or an absent value resolves to the versioned-aware typings.
noncurrentVersionExpirationDaysnumberDays after which noncurrent (superseded) object versions are permanently expired, bounding the storage cost that versioning would otherwise let grow unbounded. Only applies when versioning is enabled (the default). Default: 90. Must be a positive integer — a non-positive or non-integer value throws at synth. There is no disable sentinel; to drop the rule entirely, disable versioning (versioned: false). Ignored by the mock and browser runtimes.
corsRulesCorsRule[]CORS rules for browser-based access. Default: none — browsers block every cross-origin fetch() to the deployed bucket. Required whenever browser code fetches a presigned URL directly (download() / upload() on a file handle, or fetch() on a getUrl() / putUrl() URL) against a deployed sandbox or production stack. Not inherited from the stack's defaults.allowedOrigins or the Hosting domain. Ignored when bucket wraps an existing bucket. See Browser Uploads and Downloads (CORS), and CorsRule for the wildcard-origin synth guard.
lifecycleRulesLifecycleRule[]Lifecycle rules for automatic expiration or storage class transitions.
accessLoggingbooleanEnable S3 server access logging. Default: falls back to the stack posture (BlocksDefaults.storageAccessLogging, then BlocksDefaults.accessLogging) when omitted — so a stack can opt every FileBucket into logging without a per-block flag. When enabled, a dedicated, locked-down log bucket is provisioned (all public access blocked, S3-managed encryption, SSL enforced) and the main bucket delivers its access logs there under the access-logs/ prefix. Access logs expire automatically after the stack posture's logRetention (ONE_WEEK in sandbox / ONE_YEAR in production; RetentionDays.INFINITE keeps them indefinitely). Ignored by the mock and browser runtimes.
encryption'aws-managed' | 'customer-managed' | ExternalKmsKeyRefServer-side encryption at rest. Default: 'aws-managed' (SSE-S3). 'customer-managed' creates a dedicated KMS key for this bucket (rotation on, removed with the bucket); FileBucket.fromKmsKey(arn) uses a key you already own. Both use SSE-KMS with S3 Bucket Keys. See Encryption with KMS. Ignored, with a synth warning, when bucket wraps an existing bucket. Ignored by the mock and browser runtimes.
objectLock{ mode: 'governance' | 'compliance'; retentionDays: number }Enable S3 Object Lock with a default retention for every new object version. Default: off. Requires versioning and a retained bucket (removalPolicy: 'retain' in a sandbox); both are checked at synth. Can only be set when the bucket is first created. Presigned uploads (putUrl, createUploadHandle) are not supported on these buckets — upload with put(). See Object Lock. Ignored, with a synth warning, when bucket wraps an existing bucket. Validated, but not enforced, by the mock.
bucketExternalBucketRefWrap an existing S3 bucket instead of creating one.
loggerChildLoggerOptional logger for internal operations. When omitted, a default error-level logger is created.
removalPolicy'destroy' | 'retain'CDK removal behavior for the underlying S3 bucket. When omitted, it falls back to the stack posture default (BlocksDefaults.removalPolicy — DESTROY in sandbox, RETAIN in production); pass 'destroy' or 'retain' to set it explicitly per-block. 'destroy' also enables autoDeleteObjects so the bucket can be emptied on teardown. Ignored by the mock and browser runtimes.

All FileBucket-provisioned buckets enforce TLS unconditionally (enforceSSL: true) — CDK attaches a bucket policy denying any request where aws:SecureTransport is false, closing the in-transit exposure gap. This is not configurable.

The S3 bucket name is derived from the scope chain: the FileBucket id joined to its parent scope ids with -. If that name fits S3's 63-character limit, it is used as-is. If it is longer, it is shortened to the start of the name plus - and an 8-character hash. The shortened name is the same on every deploy and different from the names of other buckets. A name that breaks any other S3 naming rule (for example, one with uppercase letters) still fails cdk synth with a ValidationFailed error. To read the resolved name, call getSdkIdentifiers(bucket).bucketName (from @aws-blocks/blocks).

PutOptions

OptionTypeDescription
contentTypestringMIME type (e.g., image/png). Default: application/octet-stream.
metadataRecord<string, string>Custom metadata key-value pairs.
cacheControlstringCache-Control header value.

Presigned URL options

getUrl, putUrl, getFileHandle, and createUploadHandle accept expiresIn in seconds. It must be an integer from 1 to 604800 (seven days); the default is 3600.

CorsRule

CORS configuration for browser-based access. Supplied via the corsRules option.

FieldTypeDescription
allowedOriginsstring[]Origins permitted to make cross-origin requests (e.g., ['https://app.example.com']).
allowedMethods('GET' | 'PUT' | 'POST' | 'DELETE' | 'HEAD')[]HTTP methods permitted for cross-origin requests.
allowedHeadersstring[]Optional. Request headers permitted in the actual request.
exposedHeadersstring[]Optional. Response headers exposed to the browser.
maxAgenumberOptional. Seconds the browser may cache the preflight response.

Wildcard origins and mutating methods: a CORS rule that combines a wildcard origin ('*') with a mutating method (PUT, POST, or DELETE) throws at synth — it would let any site issue state-changing cross-origin requests. Specify explicit origins (e.g. ['https://app.example.com']) for mutating methods. A wildcard origin with only safe methods (GET/HEAD) is allowed.

LifecycleRule

Lifecycle configuration for automatic expiration or storage-class transitions. Supplied via the lifecycleRules option.

FieldTypeDescription
prefixstringOptional. Prefix filter — the rule applies only to keys starting with this prefix. Omit to apply to all objects.
expirationDaysnumberOptional. Days after creation to expire (delete) objects.
transitionToIaDaysnumberOptional. Days after creation to transition objects to Infrequent Access storage.

FileVersionInfo

Returned by listVersions() — one entry per object version, newest first.

FieldTypeDescription
versionIdstringThe version identifier.
lastModifiedDateWhen this version was created.
sizenumberSize in bytes.
isCurrentbooleanWhether this is the current (latest) version.

Error Handling

get() returns null for a missing file — it does not throw FileNotFound. Check for null:

const file = await bucket.get('missing.txt');
if (!file) {
  // file does not exist
}

FileBucketErrors are thrown by other operations and matched with isBlocksError:

Constanterror.nameThrown when
FileBucketErrors.FileNotFoundNoSuchKeySurfaced by presigned-URL 404s etc. Not thrown by get(), which returns null.
FileBucketErrors.FileTooLargeEntityTooLargeFile exceeds size limits.
FileBucketErrors.VersionNotFoundNoSuchVersionThrown by restoreVersion() when the target versionId cannot be resolved.
FileBucketErrors.PresignedUploadUnsupportedPresignedUploadUnsupportedThrown by putUrl() and createUploadHandle() on a bucket with objectLock — see Object Lock.
import { isBlocksError } from '@aws-blocks/core';
import { FileBucketErrors } from '@aws-blocks/bb-file-bucket';

try {
  await bucket.restoreVersion('report.pdf', 'non-existent-version');
} catch (e: unknown) {
  if (isBlocksError(e, FileBucketErrors.VersionNotFound)) {
    // that version does not exist
  }
  throw e;
}

Examples

Basic Put/Get

const bucket = new FileBucket(scope, 'uploads');

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async uploadFile(name: string, content: string) {
    await bucket.put(name, content, { contentType: 'text/plain' });
  },
  async getFile(name: string) {
    const file = await bucket.get(name);
    if (!file) return null;
    return { contentType: file.contentType, size: file.size };
  },
}));

Presigned Upload URL

const bucket = new FileBucket(scope, 'media');

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async getUploadUrl(path: string) {
    const url = await bucket.putUrl(path, {
      expiresIn: 600,
      contentType: 'image/jpeg',
    });
    return { uploadUrl: url };
  },
}));

Browser Uploads and Downloads (CORS)

Presigned URLs and file handles are fetched by the browser directly from S3, which is a different origin from your frontend. The deployed bucket must therefore allow your frontend's origin through corsRules. FileBucket configures no CORS by default, and — unlike the API endpoint — it does not pick up the stack's defaults.allowedOrigins or the Hosting domain. Without a matching rule, every browser upload or download against a deployed sandbox or production stack fails with:

Access to fetch at 'https://<bucket>.s3.<region>.amazonaws.com/...' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

For upload() / PUT, the browser fails at the preflight instead, and the message continues with Response to preflight request doesn't pass access control check: before the same text.

npm run dev does not surface this: the local dev file-server accepts any origin, so the error first appears after you deploy.

List every origin your frontend is served from — the local dev frontend (http://localhost:3000, when it talks to a deployed sandbox) and your deployed app's origin. With Hosting, that is the stack's HostingUrl output: your custom domain if you configured one, otherwise the generated https://<id>.cloudfront.net domain, which only exists after the first deploy (deploy once, copy HostingUrl into corsRules, then redeploy):

const bucket = new FileBucket(scope, 'uploads', {
  corsRules: [{
    allowedOrigins: ['http://localhost:3000', 'https://app.example.com'],
    allowedMethods: ['GET', 'PUT'],
    allowedHeaders: ['Content-Type'],
  }],
});

aws-blocks/index.ts is shared by sandbox and production deploys, so this rule also allows http://localhost:3000 on the production bucket. Only a sandbox needs it; leaving it in production is low-risk because presigned URLs authenticate through their query-string signature, not ambient browser credentials.

  • GET covers download() and fetch() on a getUrl() URL; PUT covers upload() and a putUrl() URL.
  • allowedHeaders must list every header the browser sends. A cross-origin PUT always triggers a CORS preflight, and S3 rejects the preflight if a request header — such as the Content-Type that upload() sends — is not allowed.
  • allowedOrigins entries are S3 origin strings (scheme + host + port, no trailing slash), optionally with a single * wildcard such as https://*.example.com — not the regex patterns used by defaults.allowedOrigins.
  • Use exact origins for PUT, POST, and DELETE. A bare '*' origin with a mutating method throws at synth (see CorsRule); a partial wildcard such as https://* is not caught, so avoid it for mutating methods.
  • Plain navigations to a getUrl() URL (<img src>, <a href>) are not CORS requests and work without a rule; fetch(), download(), and upload() do need one.
  • exposedHeaders (for example ETag or x-amz-version-id) is only needed when custom fetch() code reads response headers; the built-in handles don't. Every PUT preflights, so a maxAge (for example 3000) saves a round trip per upload.
  • CORS is bucket configuration: redeploy after changing corsRules.
  • corsRules is ignored when bucket wraps an existing bucket (see Wrapping an Existing Bucket) — configure CORS on that bucket itself.

Listing Files with Scan

const bucket = new FileBucket(scope, 'reports');

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async listReports(yearMonth: string) {
    const files: FileInfo[] = [];
    for await (const file of bucket.scan({ prefix: `reports/${yearMonth}/` })) {
      files.push(file);
    }
    return files;
  },
}));

Batch Delete

const bucket = new FileBucket(scope, 'tmp');

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async cleanupTemp(paths: string[]) {
    await bucket.deleteBatch(paths);
  },
}));

Wrapping an Existing Bucket

const bucket = new FileBucket(scope, 'legacy', {
  bucket: FileBucket.fromExisting('my-existing-bucket'),
});

A wrapped bucket does not receive FileBucket's secure defaults. When you supply bucket, FileBucket binds to the existing bucket as-is and returns early — none of the secure defaults it normally applies are applied: not enforceSSL, versioning / noncurrent-version expiration, server access logging, blockPublicAccess, encryption, nor corsRules or the wildcard-CORS guard. You own that bucket's security posture; configure these on the bucket itself (or via its own CDK construct) before wrapping it.

Versioned Bucket

Versioning is on by default, so the version-aware methods (listVersions, restoreVersion, and optional versionId on get/delete/getUrl/getFileHandle) are available without any option. Pass versioned: false to opt out (which also removes the versionId option typings). The example below passes versioned: true explicitly for clarity:

const bucket = new FileBucket(scope, 'docs', { versioned: true });

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async upload(name: string, content: string) {
    await bucket.put(name, content);
  },
  async getVersion(name: string, versionId?: string) {
    return await bucket.get(name, versionId ? { versionId } : undefined);
  },
  async listVersions(name: string) {
    return await bucket.listVersions(name);
  },
  async restore(name: string, versionId: string) {
    await bucket.restoreVersion(name, versionId);
  },
}));

Access Logging

Opt in to S3 server access logging. A dedicated, locked-down log bucket is provisioned and access logs expire automatically after the stack posture's logRetention (ONE_WEEK in sandbox, ONE_YEAR in production):

// Logs delivered to a separate locked-down bucket.
// Retention follows the stack posture's logRetention default — not a per-block option.
const bucket = new FileBucket(scope, 'uploads', {
  accessLogging: true,
});

To turn S3 access logging on for every FileBucket without enabling API Gateway access logging, set storageAccessLogging on the stack defaults:

await BlocksStack.create(app, stackName, {
  // ...
  defaults: { ...BlocksPresets.production, storageAccessLogging: true },
});

Encryption with KMS

Objects are encrypted with S3-managed keys (SSE-S3) by default. To control the key yourself, use a customer-managed KMS key:

// A dedicated key for this bucket (yearly rotation, removed with the bucket).
const invoices = new FileBucket(scope, 'invoices', { encryption: 'customer-managed' });

// Or share a key you already own across buckets and tables.
const key = FileBucket.fromKmsKey('arn:aws:kms:us-east-1:111122223333:key/abcd-1234');
const exports = new FileBucket(scope, 'exports', { encryption: key });

The runtime role is granted kms:Decrypt / kms:GenerateDataKey* on the key, so put/get and presigned URLs keep working. For a key you bring yourself, its key policy must delegate to IAM in the account (the default key policy does). KMS adds a monthly key charge; S3 Bucket Keys keep per-request KMS charges low.

Object Lock

Protect files from being overwritten or permanently deleted for a fixed period (write-once-read-many):

const auditLog = new FileBucket(scope, 'audit-log', {
  objectLock: { mode: 'governance', retentionDays: 365 },
});

Every new version is locked for retentionDays. delete(path) still hides the file (S3 adds a delete marker), but permanently deleting a locked version with delete(path, { versionId }) fails until the lock expires. In 'compliance' mode nobody, including the account root user, can remove a locked version early; 'governance' mode allows principals with s3:BypassGovernanceRetention to do so.

Upload through the backend. S3 requires a signed Content-MD5 or checksum header on every upload to an Object Lock bucket. A presigned URL is created before the file is known, so it can't carry one, and S3 rejects the upload. On these buckets putUrl() and createUploadHandle() throw FileBucketErrors.PresignedUploadUnsupported (locally and on AWS) rather than return a URL that fails. put() works, because the SDK adds the checksum. Downloads (getUrl(), getFileHandle()) are unaffected.

Object Lock can only be turned on when the bucket is first deployed, and can't be turned off. Locked objects also block teardown, so the bucket must be retained — pass removalPolicy: 'retain' when using Object Lock in a sandbox.

Best Practices

  • Use path prefixes to organize files (e.g., uploads/{userId}/, reports/). If a segment can contain URL-shaped or special characters (e.g. an OIDC userId of ${iss}:${sub} like https://issuer:sub), wrap it in encodeURIComponent() first — an un-encoded // is an empty path segment, which FileBucket rejects (ValidationFailed) on both the local mock and AWS so a key can't mean one object locally and another in production.
  • Set contentType on put() to ensure correct MIME handling on download
  • Use presigned URLs (getUrl / putUrl) for direct browser upload/download — and set corsRules for your frontend's origins, or the browser blocks them once deployed (see Browser Uploads and Downloads (CORS))
  • Prefer scan({ prefix }) over unscoped scan() to limit enumeration cost
  • For browser uploads/downloads returned from API methods, prefer createUploadHandle/getFileHandle over raw presigned URLs — they encode the fetch protocol into typed methods so the client can't misuse them
  • Use deleteBatch() instead of looping delete() for bulk operations
  • Versioning is on by default; noncurrent (superseded) versions are automatically expired after 90 days (noncurrentVersionExpirationDays, default 90) so version history doesn't grow storage cost unbounded. Tune the window per-block, or disable versioning (versioned: false) to drop the expiration rule along with versioning.

Scaling & Cost (AWS)

  • Billing: Per-request plus storage — no provisioned throughput
  • Latency: First-byte latency typically 100–200 ms
  • Throughput: Scales automatically, 5,500 GET and 3,500 PUT requests/second per prefix
  • Object size limit: 5 TB per object
  • Cost: ~$0.005 per 1,000 PUT requests, ~$0.0004 per 1,000 GET requests, ~$0.023/GB/month storage
  • Durability: 99.999999999% (11 nines) across 3+ AZs

Local Development

Mock data persists to disk at .bb-data/{fullId}/ across dev server restarts. Internal data is segregated into sibling roots so it never collides with your keys: file bodies live under content/, metadata under meta/, and version history under versions/. Wipe with rm -rf .bb-data. Presigned URLs are served by the dev server at /.bb-file-bucket/{fullId}/{path}?token=.... Versioning is fully supported locally. Lifecycle rules and corsRules have no effect locally — the dev file-server accepts browser requests from any origin, so a missing or wrong corsRules only shows up after deploy (see Browser Uploads and Downloads (CORS)).

版本与发布记录

@aws-blocks/bb-file-bucket 共发布 14 个版本,首次发布于 2026/06/16;过去 12 个月发布了 14 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.4.0latest2026/11/025 天前
0.3.12026/10/1622 天前
0.0.0-canary.esql-canary.c93c81b.1791235863canary-esql-canary2026/10/061 个月前
0.3.02026/10/051 个月前
0.0.0-canary.esql-canary.f7f39ac.17909891272026/10/031 个月前
0.0.0-canary.ci-publish-canary-npm.f53dfdc.1790988617canary-ci-publish-canary-npm2026/10/031 个月前

关于 @aws-blocks/bb-file-bucket 的常见问题

@aws-blocks/bb-file-bucket 是什么?

File storage backed by Amazon S3.

如何安装 @aws-blocks/bb-file-bucket?

在项目目录运行 npm install @aws-blocks/bb-file-bucket,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:constructs(^10.6.0)、aws-cdk-lib(^2.257.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@aws-blocks/bb-file-bucket 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aws-blocks/bb-file-bucket 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aws-blocks/bb-file-bucket 可以商用吗?

@aws-blocks/bb-file-bucket 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@aws-blocks/bb-file-bucket 的下载量有多少?

根据 npm 官方下载统计,@aws-blocks/bb-file-bucket 在 2026/10/07 – 2026/11/05 这 30 天内被下载 64,610 次,日均约 2,154 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aws-blocks/bb-file-bucket 还在维护吗?

最新版本 0.4.0 发布于 2026/11/02(5 天前);至今共发布 14 个版本,其中过去 12 个月发布了 14 个;首次发布于 2026/06/16。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。