# @ddd-ts-kit/s3 中文文档

规范地址：https://npmcn.com/package/%40ddd-ts-kit/s3/

版本：0\.106\.0
npm 元数据更新时间：2026-10-31T21:44:36.213Z
中文译文更新时间：2026-10-31T23:22:49.404Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40ddd-ts-kit/s3
原文对照：https://npmcn.com/package/%40ddd-ts-kit/s3/?lang=original
项目主页：https://gbourgeat.github.io/ddd-ts-kit/packages/s3/
源码仓库：https://github.com/gbourgeat/ddd-ts-kit

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：56 kB，44 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.12\.0
- 运行依赖：1 个
- peer 依赖：@aws-sdk/client-s3 ^3\.1152\.0，@ddd-ts-kit/testing 0\.106\.0，@aws-sdk/lib-storage ^3\.1152\.0，@aws-sdk/s3-request-presigner ^3\.1152\.0
- 首次发布：2026-10-13T06:16:56.842Z
- 最新版本发布：2026-10-31T21:44:35.893Z
- 已发布版本数：54（近 12 个月 54 个）

## 中文简介

S3协议（AWS S3, MinIO, OVHcloud, Scaleway, Cloudflare R2）上的内核FileStorage端口适配器：流式上传，签名链接，复制，移动，列表。

## 安装

```sh
npm install @ddd-ts-kit/s3
```

也可以使用：`pnpm add @ddd-ts-kit/s3`、`yarn add @ddd-ts-kit/s3`、`bun add @ddd-ts-kit/s3`

## README

**英语** · [法语](https://npmcn.com/package/%40ddd-ts-kit/s3/README.fr.md)

# @ddd-ts-kit / s3

内核 `FileStorage` 任何S3协议提供商：AWS S3、MinIO、OVHcloud Object 仓库，Scaleway, Cloudflare R2，车库。文件的内容以部分形式流到bucket中， 永远不会在内存中完整，也永远不会在数据库中完整；骨料保持了 `StoredFile` 那 `put` 的回报。中没有 `domain/` 或者 `application/` 处理程序依赖于 `FileStorage` 端口，平台绑定此适配器。

```
npm i @ddd-ts-kit/s3 @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
```

官方AWS SDK v3的三个包是可选的对等体，根据3.1150编写。

![The use cases and FileStaging depend on the FileStorage port of the kernel; S3FileStorage implements it, maps each key under its prefix and sends its commands through an S3Client built by s3ClientOf; the client uploads and downloads straight to the bucket with signed links; the bucket holds promoted files under their keys and staged files under staging/](https://raw.githubusercontent.com/gbourgeat/ddd-ts-kit/main/docs/assets/diagrams/s3-place-in-an-application.svg)

用例使用诸如的密钥与端口对话 `appointment-documents/2026/10/7f3c`， 生成的 `TenantFileKeys`, `FileKeys` 核心：地区，年，月，然后 一个来自 `IdGenerator` (和 `tenants/<tenant>/` 在他们之前（当作品命名一个租户时）。 适配器将其前缀放在每个键(`scheduling/appointment-documents/2026/10/7f3c` 在 桶)，和 `FileStaging` 把 `staging/` 在等待提交的文件的键之前 （`scheduling/staging/appointment-documents/2026/10/9a1b`）. 文件的字节从 客户端到桶的部分，或者从API到桶的部分，而数据库只保留 参考。

## 作文根

```ts
import type { FileStorage } from '@ddd-ts-kit/kernel';
import { S3FileStorage, s3ClientOf } from '@ddd-ts-kit/s3';

declare const config: {
  readonly storage: {
    readonly endpoint?: string;
    readonly region: string;
    readonly accessKeyId: string;
    readonly secretAccessKey: string;
    readonly bucket: string;
  };
};

const { endpoint, region, accessKeyId, secretAccessKey, bucket } = config.storage;

export const files: FileStorage = new S3FileStorage({
  client: s3ClientOf({
    ...(endpoint === undefined ? {} : { endpoint }), // left out for AWS; http://localhost:9000 for MinIO
    region, // 'auto' for R2
    accessKeyId,
    secretAccessKey,
  }),
  bucket, // created by the infrastructure code, never by the application
  prefix: 'scheduling/', // optional: several applications in one bucket
});
```

`s3ClientOf` 当给定端点（MinIO和大多数自托管）时设置路径样式寻址 服务器需要校验和)，并且只在操作需要校验和时才计算校验和 SDK对每个请求发送一个校验和，一些非AWS的提供商会拒绝 这将绑定一个签名的上传链接到一个空的主体。手工构建的客户机也可以工作。

的选项 `S3FileStorage`：

| 选项 | 默认的 |  |
| --- | --- | --- |
| `client` | 要求 | 一个 `S3Client`,从 `s3ClientOf` 或者手工制作 |
| `bucket` | 要求 | 它必须存在：适配器不会创建它 |
| `prefix` | 无 | 以结尾的有效密钥 `/`，把每把钥匙放在前面；构造者拒绝另一个 |
| `partSize` | 8 MiB | 上传的每个部分的大小，至少5mib：每次上传所占用的最大内存 |
| `clock` | 系统时钟 | 给出了 `expiresAt` 带符号的链接返回 |

处理程序依赖于端口，而不是SDK：

```ts
// Instead of: the handler speaks S3, holds the whole file in memory, and names the bucket
const body = Buffer.concat(await Array.fromAsync(rows));
await s3.send(new PutObjectCommand({ Bucket: 'acme-files', Key: `exports/${id}.csv`, Body: body }));
```

```ts
import { FileKeys, FileStorage, type StoredFile } from '@ddd-ts-kit/kernel';

// Write: the port of the kernel; the platform binds S3FileStorage to it, a test InMemoryFileStorage.
export class ExportAppointments {
  constructor(
    private readonly files: FileStorage,
    private readonly keys: FileKeys,
  ) {}

  async execute(csv: AsyncIterable<Uint8Array>): Promise<{ readonly file: StoredFile; readonly link: string }> {
    const key = this.keys.next('appointment-exports');

    // Streams to the bucket in parts; beyond 50 MiB, FILE_TOO_LARGE, and nothing is left under the key.
    const file = await this.files.put(key, csv, { mediaType: 'text/csv', maxBytes: 50 * 1_048_576 });

    // A presigned GetObject, computed locally: no request reaches the bucket.
    const { url } = await this.files.signedDownloadUrl(key, { expiresInSeconds: 300, fileName: 'appointments.csv' });

    return { file, link: url };
  }
}
```

## 它是做什么的

| 端口的方法 | 在桶上 |
| --- | --- |
| `put(key, stream, { mediaType, maxBytes })` | 的多部分上传 `partSize` 部分（默认为8 MiB，每次一个在内存中）或单个 `PutObject` 当内容适合于一个部分时；内容在它的路上被计数和散列，并被拒绝 `FILE_TOO_LARGE` 除了 `maxBytes`，它将终止上传并保留前一个对象 |
| `get`， `stat` | `GetObject` 作为一条小溪， `HeadObject`； `undefined` 当对象不存在时，当无法到达服务时拒绝 |
| `delete` | `DeleteObject`,幂等 |
| `copy`， `move` | `CopyObject` 由服务（和）完成 `DeleteObject` 对于移动)，每个对象最多5 GiB |
| `list(prefix)` | `ListObjectsV2`一页接一页 |
| `signedDownloadUrl` | 一个presigned `GetObject` 回答与 `Content-Disposition: attachment`，当给定一个时使用文件名 |
| `signedUploadUrl` | 一个presigned `PutObject` 他的签名覆盖了 `content-type`：服务拒绝其他媒体类型 |

校验和(`sha256:<hex>`)由 `put`，内容流时计算；这个 巴克特没有保存它，所以 `stat` 不返回1。通过签名链接上传的文件 被测量、检查和散列了吗 `FileStaging.confirm` 内核，它把它读回来。

端口拒绝的密钥(`../escape`)被拒绝了 `INVALID_FILE_KEY`，并要求链接 一个多小时 `INVALID_FILE_URL_EXPIRY`，在任何请求之前。签名不需要请求 或者：从凭据中计算已签名的链接，并且只有提供者检查它 客户端使用它。

![An upload through a signed link: the client asks for a link, FileStaging.uploadUrl has S3FileStorage sign a PUT locally and keeps the grant on the aggregate, the client sends the file straight to the bucket, which checks the key, method, content-type and expiry but cannot cap the size; then the client confirms, and FileStaging.confirm reads the staged object back to check its size against the cap, its media type, its checksum and the scan](https://raw.githubusercontent.com/gbourgeat/ddd-ts-kit/main/docs/assets/diagrams/s3-signed-upload.svg)

客户端发送的文件，完全按照链接说，它的头是给定的：

```ts
import type { SignedUpload } from '@ddd-ts-kit/kernel';

// In the client: the signature covers the content-type, so the headers are sent as they are.
export async function sendFile(link: SignedUpload, file: Blob): Promise<void> {
  const response = await fetch(link.url, { method: link.method, headers: link.headers, body: file });

  if (!response.ok) {
    throw new Error(`The storage refused the upload: ${response.status}`); // 403 once expired, or for another type
  }
}
```

## 限制

-   签名上传链接不能限制发送内容的大小（只有预先指定的POST策略可以）： 它的 `maxBytes` 不是由服务强制执行的，并且 `FileStaging.confirm` 测量文件 服务器保留了超出授权上限的权限，并将其丢弃。
-   复制并移动，通过 `CopyObject`，协议将每个对象限制为5 GiB。
-   一次上传最多有10000个部分：每个部分有8mib，一个文件大约有78gib。提高 `partSize` 对于较大的文件。
-   的生命周期规则、版本控制、对象锁定和桶的加密都是 Bucket，由部署的基础结构代码生成，而不是由此适配器生成。

## 测试

每个规格在单元运行中运行：存储到达，通过真正的SDK，它的签名者和它的 HTTP传输，一个S3服务器，测试在它自己的进程中在一个空闲端口上启动 （`test/support/server/`）. 它在标头和签名链接中检查签名版本4，并且 像MinIO一样回答存储请求：对象，副本，逐页前缀列表， 多部分上传，已签名下载保存的名称，链接的过期（其时钟） 不用等待就继续前进)。 `fileStorageContract` 的 `@ddd-ts-kit/testing/contracts` (再出口 由 `@ddd-ts-kit/s3/testing`)运行它。与 `DDD_TS_KIT_TEST_S3_ENDPOINT`，如 管道的集成作业，相同的规范在真实服务器上运行第二次，这 让记忆中的人保持诚实。上游MinIO项目停止在 2025年：管道运行 `pgsty/minio`，从相同的来源建立，由文摘固定 （`pipeline/github/scripts/start-minio.sh`）。

```
docker run -d --name minio -p 127.0.0.1:9000:9000 -e MINIO_ROOT_USER=ddd-ts-kit -e MINIO_ROOT_PASSWORD=ddd-ts-kit-secret pgsty/minio:RELEASE.2026-08-04T00-00-00Z server /data
DDD_TS_KIT_TEST_S3_ENDPOINT=http://localhost:9000 npx vitest run packages/s3
```

相同的规格在协议的另一个服务器上运行： `DDD_TS_KIT_TEST_S3_REGION` （`us-east-1` 由 默认的), `DDD_TS_KIT_TEST_S3_ACCESS_KEY` 和 `DDD_TS_KIT_TEST_S3_SECRET_KEY` 命名其区域和 钥匙，还有水桶 `ddd-ts-kit-test` 在键可以创建时创建。手动运行一次 在Garage v2.1.0（2026-10-18）上，区域 `garage`： 34项测试通过。其他的供应商 [文档/工具/ file-storage.md](https://npmcn.com/package/docs/tools/file-storage.md) 都没有经过验证。

一个项目在其组合堆栈的MinIO上对同一个套件持有自己的绑定：每个测试 在自己的前缀下写入，因此测试共享一个bucket，而不会看到彼此的bucket 文件。

```ts
import { randomBytes } from 'node:crypto';
import { beforeEach, describe, expect, it } from 'vitest';
import { S3FileStorage, s3ClientOf } from '@ddd-ts-kit/s3';
import { fileStorageContract } from '@ddd-ts-kit/s3/testing';

const endpoint = process.env['S3_TEST_ENDPOINT'] ?? '';

// Skipped without a server; the bucket is created beforehand, as in a deployment.
fileStorageContract(
  { describe: describe.runIf(endpoint !== ''), it, expect, beforeEach },
  {
    name: 'S3FileStorage on MinIO',
    createStorage: () =>
      new S3FileStorage({
        client: s3ClientOf({ endpoint, region: 'us-east-1', accessKeyId: 'minio', secretAccessKey: 'minio-secret' }),
        bucket: 'acme-files-test',
        prefix: `run-${randomBytes(6).toString('hex')}/`,
      }),
    // The links of a real server expire in real time.
    elapse: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
  },
);
```

## 另一个供应商

谷歌云存储和Azure Blob存储使用自己的协议。连接器：用于…之一的连接器 它们是一个类扩展 `FileStorage` 通过的内核 `fileStorageContract` 反对 真实服务或其模拟器（fake-gcs-server, Azurite）：参见 [文档/工具/ file-storage.md](https://npmcn.com/package/docs/tools/file-storage.md).

## 常见问题

### @ddd-ts-kit/s3 是什么？

S3协议（AWS S3, MinIO, OVHcloud, Scaleway, Cloudflare R2）上的内核FileStorage端口适配器：流式上传，签名链接，复制，移动，列表。

### 如何安装 @ddd-ts-kit/s3？

在项目目录运行 npm install @ddd-ts-kit/s3，或使用 pnpm add / yarn add / bun add。

### @ddd-ts-kit/s3 支持 TypeScript 吗？

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

### @ddd-ts-kit/s3 支持 ESM 还是 CommonJS？

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

### @ddd-ts-kit/s3 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.12\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @ddd-ts-kit/s3 可以商用吗？

@ddd-ts-kit/s3 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @ddd-ts-kit/s3 还在维护吗？

最新版本 0\.106\.0 发布于 2026/11/01（2 天前）；至今共发布 54 个版本，其中过去 12 个月发布了 54 个；首次发布于 2026/10/13。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

