> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squarecloud.app/llms.txt
> Use this file to discover all available pages before exploring further.

# S3 兼容性

> 通过 S3 兼容网关在 Blob Storage 上使用 aws-cli、boto3、rclone 和 AWS SDK：endpoint、bucket、支持的操作与限制。

Blob Storage 在 `https://s3-blob.squarecloud.app` 上提供 S3 API。任何允许设置自定义 endpoint 的工具都可以使用：aws-cli、boto3、AWS SDK for JavaScript、rclone、Cyberduck 以及大多数备份工具。

## 凭证

S3 工具使用访问密钥对为请求签名。请从 [S3 凭证](/zh/blob-reference/endpoint/s3-credentials)获取：

```bash theme={null}
curl https://blob.squarecloud.app/v1/s3/credentials \
  --header 'Authorization: YOUR_API_KEY'
```

该密钥对**由你的 API key 派生**，无需单独管理：撤销或重新生成 key 会对密钥对产生同样的效果，并且密钥对拥有相同的 scope。只有 `blob:read` 的 key 会得到只读的密钥对。

只要 API key 不变，密钥对就不会变，因此获取一次后将其保存在你的密钥管理器或环境变量中。不要在每次启动时调用该路由：它每小时只接受 10 次请求。

| 设置       | 值                                                            |
| -------- | ------------------------------------------------------------ |
| Endpoint | `https://s3-blob.squarecloud.app`                            |
| 区域       | `auto`                                                       |
| 寻址方式     | Path-style（`https://s3-blob.squarecloud.app/<bucket>/<key>`） |
| 签名       | AWS Signature Version 4                                      |

## Bucket

你的账户可以看到三个固定的 bucket。你无法创建或删除 bucket。

| Bucket    | 内容                                                            | 访问权限     |
| --------- | ------------------------------------------------------------- | -------- |
| `public`  | 公开文件，通过 `https://blob.squarecloud.dev/pub/<user_id>/<key>` 分发 | 读和写      |
| `private` | 私有文件，没有公开 URL                                                 | 读和写      |
| `legacy`  | 2026 年 9 月更新之前上传的旧版文件                                         | 读取、列出和删除 |

`public` 或 `private` bucket 中的 key 是**不含**你的 user id 的对象路径：`public` bucket 中的 `images/logo.png` 就是 REST API 上的对象 `pub/<user_id>/images/logo.png`。通过 S3 写入的文件会出现在 REST API 和控制台中，反之亦然。

## 配置

<CodeGroup>
  ```bash aws-cli theme={null}
  aws configure set aws_access_key_id SQ2_...
  aws configure set aws_secret_access_key ...
  aws configure set region auto

  aws s3 cp ./logo.png s3://public/images/logo.png \
    --endpoint-url https://s3-blob.squarecloud.app
  aws s3 ls s3://public/images/ --endpoint-url https://s3-blob.squarecloud.app
  ```

  ```python boto3 theme={null}
  import boto3
  from botocore.config import Config

  s3 = boto3.client(
      "s3",
      endpoint_url="https://s3-blob.squarecloud.app",
      aws_access_key_id="SQ2_...",
      aws_secret_access_key="...",
      region_name="auto",
      config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
  )

  s3.upload_file("report.pdf", "private", "reports/2026/report.pdf")
  url = s3.generate_presigned_url(
      "get_object",
      Params={"Bucket": "private", "Key": "reports/2026/report.pdf"},
      ExpiresIn=3600,
  )
  ```

  ```javascript AWS SDK v3 theme={null}
  import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

  const s3 = new S3Client({
    endpoint: "https://s3-blob.squarecloud.app",
    region: "auto",
    forcePathStyle: true,
    credentials: { accessKeyId: "SQ2_...", secretAccessKey: "..." },
  });

  await s3.send(new PutObjectCommand({
    Bucket: "public",
    Key: "images/logo.png",
    Body: buffer,
    ContentType: "image/png",
  }));
  ```

  ```ini rclone theme={null}
  [squarecloud]
  type = s3
  provider = Other
  access_key_id = SQ2_...
  secret_access_key = ...
  endpoint = https://s3-blob.squarecloud.app
  region = auto
  force_path_style = true
  ```
</CodeGroup>

<Note>boto3 生成预签名 URL 时需要 `signature_version="s3v4"`：否则 `generate_presigned_url` 会使用较旧的 SigV2 签名，而网关不接受 SigV2。预签名 URL 的有效期最长为 7 天（604800 秒）。</Note>

网关的响应永远不会在边缘缓存。预签名 URL 会在过期、API key 被撤销或对象被删除时立即失效。

## 支持的操作

| 领域     | 操作                                                                                                                                                                                                   |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bucket | `ListBuckets`、`HeadBucket`、`GetBucketLocation`。对已存在的 bucket 执行 `CreateBucket` 会成功；bucket 中仍有文件时，`DeleteBucket` 返回 `BucketNotEmpty`。                                                                  |
| 列表     | `ListObjects` 和 `ListObjectsV2`，支持 `delimiter`、`prefix` 和 `encoding-type=url`。                                                                                                                       |
| 对象     | `HeadObject`、`GetObject`（支持 `Range`、`If-Match`、`If-None-Match`、`If-Modified-Since`）、`PutObject`（单次请求最大 100 MB）、`CopyObject`（`COPY` 和 `REPLACE` 元数据指令）、`DeleteObject`、`DeleteObjects`（最多 1000 个 key）。 |
| 分段上传   | `CreateMultipartUpload`、`UploadPart`（每段 5 MB 到 80 MB，最后一段除外）、`UploadPartCopy`、`CompleteMultipartUpload`、`AbortMultipartUpload`、`ListParts`、`ListMultipartUploads`。对象最大 10 GiB。                       |
| 完整性    | 始终校验 `Content-MD5`，即使同时发送了 `x-amz-checksum-*` 请求头也是如此。`x-amz-checksum-crc32`、`-sha1` 和 `-sha256` 同样会被校验；不匹配时返回 `BadDigest`。`crc32c` 和 `crc64nvme` 会被接受但不校验。支持 `aws-chunked` 请求体。                     |
| 兼容性    | `GetBucketVersioning`、`GetBucketAcl`、`GetObjectAcl` 以及标签读取操作返回固定值，以便探测这些操作的工具继续正常工作。                                                                                                                 |

Bucket 策略、CORS、生命周期、网站、加密、对象锁定、版本、日志、通知、复制、ACL 和标签写入、按 `partNumber` 的 `GET` 以及浏览器 POST 表单上传均返回 `501 NotImplemented`。生命周期规则请使用[账户设置](/zh/blob-reference/endpoint/settings-put)。

## Key

* Key 是字面路径。一个 key 最长约 1000 字节：1024 字节的上限也把账户前缀计算在内。更长的 key 会返回 `KeyTooLongError`，其消息会给出你可用的确切字节数。路径段不能为空，也不能是 `.` 或 `..`。
* 以 `/` 结尾的 0 字节 key 是文件夹标记，与 AWS 控制台创建文件夹的方式相同。
* 分发时的 `Content-Type` 由扩展名推导，与 REST API 相同。可执行文件会被拒绝并返回 `InvalidArgument`，`.html`、`.svg` 和 `.xml` 以下载方式分发。

## 元数据、缓存与过期

* `x-amz-meta-*` 请求头在 **Pro 和 Enterprise** 计划上会被保留（最多 5 个键和 512 字节，否则返回 `MetadataTooLarge`）。在其他计划上它们会被丢弃。
* `Cache-Control` 和 `Content-Disposition` 会被保留。在 Enterprise 之外，不缓存的 `Cache-Control`（`no-cache`、`no-store` 或 `max-age=0`）会被拒绝并返回 `AccessDenied`。
* [按前缀设置的规则](/zh/blob-reference/endpoint/settings-put)同样适用于通过 S3 写入的对象，包括自动删除。规则的 `max_size` 和 `extensions` 仅适用于 REST 上传。

## 限制

| 限制                                   | 值                                                                   |
| ------------------------------------ | ------------------------------------------------------------------- |
| 每个账户的请求（每 10 秒）                      | **Hobby** 100 · **Standard** 200 · **Pro** 400 · **Enterprise** 600 |
| 服务端复制（`CopyObject`、`UploadPartCopy`） | 每 10 秒 20 次                                                         |
| `DeleteObjects` 删除的 key              | 每 10 秒 2000 个                                                       |
| 未完成的分段上传                             | 每个账户 32 个，与 REST 分块上传共享                                             |
| 单次 `PutObject`                       | 100 MB                                                              |
| 分段（`UploadPart`）                     | 5 MB 到 80 MB，最后一段除外                                                 |
| 对象大小                                 | 10 GiB                                                              |
| 无效凭证                                 | 失败次数过多会将该 IP 封禁几分钟                                                  |

超出限制后，网关返回 `SlowDown`（HTTP 503），AWS SDK 会自动退避并重试。S3 请求**不计入**你所在计划的 API 请求限制。

在循环重试之前，请先检查密钥对：发送过多无效凭证的 IP 会被封禁几分钟。

### 分段大小

分段上传工具会自动拆分大文件；请让每个分段保持在 **80 MB 或以下**。AWS CLI（8 MB）和 rclone（5 MB）的默认值已经符合要求。

<CodeGroup>
  ```bash aws-cli theme={null}
  aws configure set default.s3.multipart_chunksize 64MB
  ```

  ```python boto3 theme={null}
  from boto3.s3.transfer import TransferConfig

  config = TransferConfig(multipart_chunksize=64 * 1024 * 1024)
  s3.upload_file("backup.tar.gz", "private", "backups/backup.tar.gz", Config=config)
  ```

  ```javascript AWS SDK v3 theme={null}
  import { Upload } from "@aws-sdk/lib-storage";

  await new Upload({
    client: s3,
    params: { Bucket: "private", Key: "backups/backup.tar.gz", Body: stream },
    partSize: 64 * 1024 * 1024, // 最大 80 MB
  }).done();
  ```
</CodeGroup>

写入需要付费计划。没有付费计划时，以及在只读的 `legacy` bucket 上，写入会返回 `AccessDenied`。存储配额与 REST API 一样适用。

## 错误

网关返回标准的 S3 XML 错误，因此 SDK 可以原生处理：

| 错误                                                  | 触发情况                                               |
| --------------------------------------------------- | -------------------------------------------------- |
| `AccessDenied`                                      | 没有付费计划、只读 key、写入 `legacy`，或使用了计划之外的选项。             |
| `NoSuchBucket`                                      | Bucket 不是 `public`、`private` 或 `legacy`。           |
| `NoSuchKey`                                         | 对象不存在。                                             |
| `InvalidArgument`                                   | 被屏蔽的文件类型（可执行文件和安装程序）或格式错误的请求头。                     |
| `QuotaExceeded`                                     | 账户已达到其内置存储配额。                                      |
| `EntityTooLarge` / `EntityTooSmall`                 | `PutObject` 超过 100 MB、分段超过 80 MB，或非最后一段的分段小于 5 MB。 |
| `InvalidPart` / `InvalidPartOrder` / `NoSuchUpload` | 分段上传完成时分段有误，或上传已不存在。                               |
| `BadDigest`                                         | 请求体与 `Content-MD5` 或 `x-amz-checksum-*` 请求头不匹配。    |
| `PreconditionFailed`                                | `If-Match` 或 `If-None-Match` 条件未满足。                |
| `MetadataTooLarge`                                  | 元数据超过 5 个键或 512 字节。                                |
| `KeyTooLongError`                                   | Key 过长（约 1000 字节）。消息会说明你账户的确切上限。                   |
| `SlowDown`                                          | HTTP 503：达到了速率限制或未完成上传数量限制。SDK 会自动使用退避策略重试。        |
| `NotImplemented`                                    | 不支持该操作（见上文）。                                       |
