Skip to content
smallPict
免费开始

自带存储 + CDN

在 API Velocity 和 Momentum 套餐中连接您自己的对象存储和 CDN:各服务商的设置方法、最小权限、连接检查,以及切换模式时会发生什么。

适用套餐: API Velocity 和 Momentum 套餐。不适用于 Ignite(不含 CDN)、开发者沙盒(沙盒文件是临时的)或 WordPress 套餐(使用托管的 smallPict CDN)。

简而言之: 在 BYO 模式下,smallPict 会将您的原图和优化后的图片写入 您的 存储桶,并由 您的 CDN 分发。存储和 CDN 始终一起连接:不能只接入其中一个。


什么是 BYO

Velocity 和 Momentum 有两种分发模式。您可以在 控制台 → CDN 与存储 中选择其一。

  • 托管(默认): smallPict 将您的原图保存在其加密存储中(90 天后转入长期归档),并通过 cdn.smallpict.app 分发优化后的图片。无需任何设置。
  • 使用自己的存储 + CDN(BYO): 您连接自己账户中的一个 S3 兼容存储桶,以及自己账户中的一个 CDN。每个新任务都会将原图和优化后的文件写入您的存储桶,API 返回的是您 CDN 域名上的 URL。

之所以要求两者一起连接,是因为 smallPict 必须能完成分发的两部分工作:写入文件,以及从缓存中清除过期副本。只有存储桶而没有 CDN,缓存清除请求将无处可发;只有 CDN 而没有存储桶,则没有可分发的内容。


托管与 BYO 对比

托管(默认)使用自己的存储 + CDN
原图smallPict 加密存储您存储桶中的 originals/<job_id>/<file>
优化后的文件从 cdn.smallpict.app 分发您存储桶中的 optimized/<job_id>.<ext>,从您的 CDN 域名分发
存储配额Velocity 50 GB,Momentum 100 GB不计入。费用由您向存储服务商支付。
CDN 流量Velocity 30 GB/月,Momentum 200 GB/月不计入。费用由您向 CDN 服务商支付。
转换次数计入您的套餐计入您的套餐
smallPict 保留的副本有,在您的账户有效期间保留无。仅有用于处理的临时上传文件,会在 24 小时内删除。
缓存清除自动自动,通过您 CDN 的 API
CDN 性能、域名和费用smallPict您自己。smallPict 不对您 CDN 的性能、域名或账单负责。

支持的服务商

存储(现已可用): Amazon S3、Cloudflare R2、Google Cloud Storage(S3 互操作)、Alibaba Cloud OSS、Tencent Cloud COS、DigitalOcean Spaces、SumoPod Storage、MinIO,以及通过自定义 Endpoint 接入的任何其他 S3 兼容服务(例如 Hetzner、Vultr 或 Wasabi)。

CDN(现已可用): Cloudflare 和 Amazon CloudFront。

计划中: Azure Blob Storage 及其他 CDN。您可以在 云服务商 页面申请抢先体验。


开始之前

  1. 您使用的是 API Velocity 或 Momentum 套餐,并以账户管理员身份登录。
  2. 您已有一个存储桶,以及一个通过 HTTPS 分发该存储桶中文件的 CDN。
  3. 存储 Endpoint 可通过 HTTPS 从互联网访问。私有、内部及纯 HTTP 的 Endpoint 会被拒绝。
  4. 您已为 smallPict 创建了权限范围最小的密钥(见下文 最小权限)。不要使用账户的 root 或管理员密钥。

然后打开 控制台 → CDN 与存储,选择 使用自己的存储 + CDN,填写两个部分并点击 保存存储 + CDN。smallPict 会运行连接检查(见下文),只有检查通过才会将您切换到 BYO。


设置指南

A. Amazon S3 + Amazon CloudFront

  1. 创建存储桶,选择所需区域,例如在 ap-southeast-1 中创建 my-images。保持 Block Public Access 开启。
  2. 创建 CloudFront 分配(distribution),以该存储桶作为源站。使用 Origin access control (OAC),让 CloudFront 能在存储桶保持私有的情况下读取它,并应用 CloudFront 提供的存储桶策略。
  3. 可选:使用自己的域名。 为分配添加备用域名(例如 images.example.com)和证书,并将 DNS 记录指向该分配。
  4. 为存储创建一个 IAM 用户(或角色),使用以下策略:
JSON
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Action": [        "s3:PutObject",        "s3:GetObject",        "s3:DeleteObject"      ],      "Resource": "arn:aws:s3:::my-images/*"    }  ]}
  1. 为 CloudFront 创建一个 IAM 用户(可以是同一个用户),使用以下策略。cloudfront:GetDistribution 为可选项;有了它,当您将 CDN 域名留空时,smallPict 可以自动检测该分配的域名。
JSON
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Action": [        "cloudfront:CreateInvalidation",        "cloudfront:GetDistribution"      ],      "Resource": "arn:aws:cloudfront::123456789012:distribution/E2QWRUHAPOMQZL"    }  ]}
  1. 在控制台中:
    • 存储服务商选择 Amazon S3,存储桶 my-images,区域 ap-southeast-1,Endpoint 留空(使用该区域的标准 Endpoint),并填写存储的 access key ID 和 secret。
    • CDN 选择 Amazon CloudFront,填写 Distribution ID(例如 E2QWRUHAPOMQZL)、CloudFront 的 access key ID 和 secret,以及可选的 CDN 域名(https://images.example.com)。将域名留空则使用该分配自身的 *.cloudfront.net 域名。

B. Cloudflare R2 + Cloudflare

  1. 创建一个 R2 存储桶,例如 my-images。
  2. 为存储桶连接自定义域名(R2 → 您的存储桶 → Settings → Custom Domains),例如同一 Cloudflare 账户下某个区域中的 images.example.com。对该域名的请求会经过 Cloudflare 缓存。生产环境中不要使用 r2.dev 开发 URL。
  3. 创建 R2 API 令牌(R2 → Manage API tokens),授予 Object Read & Write 权限,并仅限此存储桶。复制显示的 Access Key ID 和 Secret Access Key。
  4. 创建 Cloudflare API 令牌(My Profile → API Tokens),为该区域授予 Zone → Cache Purge → Purge 权限。Zone → Zone → Read 为可选。
  5. 在控制台中:
    • 存储服务商选择 Cloudflare R2,存储桶 my-images,Endpoint https://<account_id>.r2.cloudflarestorage.com(显示在存储桶的 S3 API 信息中),区域 auto,以及 R2 的 Access Key ID 和 Secret Access Key。
    • CDN 选择 Cloudflare,填写 Zone ID(域名的 Overview 页面,API 区域)、API 令牌和 CDN 域名 https://images.example.com。

C. Google Cloud Storage + Cloudflare

Google Cloud Storage 通过其 S3 兼容的 XML API 并使用 HMAC 密钥连接。

  1. 创建存储桶,例如 my-images。
  2. 创建服务账号,并仅在此存储桶上授予其 Storage Object User(roles/storage.objectUser)角色。
  3. 为服务账号创建 HMAC 密钥: Cloud Storage → Settings → Interoperability → Create a key for a service account。复制 access ID 和 secret。
  4. 让您的 CDN 可以读取优化后的文件。 Cloudflare 通过 HTTPS 从 Cloud Storage 获取文件,因此 optimized/ 下的对象必须可公开读取。在统一存储桶级访问(uniform bucket-level access)下,向 allUsers 授予 Storage Object Viewer 角色会使整个存储桶可读,包括 originals/。如果原图必须保持私有,请在前面加一层身份验证(例如一个为发往存储桶的请求签名的 Cloudflare Worker)。
  5. 将一条开启代理的 Cloudflare DNS 记录指向 Cloud Storage,例如 images.example.com。可以按主机名命名存储桶并使用指向 c.storage.googleapis.com 的代理 CNAME,也可以使用 Cloudflare Origin Rule 将请求发送到 storage.googleapis.com,并在路径开头加上存储桶名称。
  6. 创建 Cloudflare API 令牌,为该区域授予 Zone → Cache Purge → Purge 权限(Zone → Zone → Read 可选)。
  7. 在控制台中: 存储服务商选择 Google Cloud Storage,填写存储桶、Endpoint https://storage.googleapis.com、区域 auto、HMAC access ID 和 secret;CDN 选择 Cloudflare,填写 Zone ID、API 令牌和 CDN 域名 https://images.example.com。

D. MinIO 或任何 S3 兼容存储 + Cloudflare

本节适用于 MinIO 以及 Hetzner、Wasabi、Vultr、SumoPod、Alibaba Cloud OSS、Tencent Cloud COS 和 DigitalOcean Spaces 等 S3 兼容服务。

服务商在控制台中选择Endpoint说明
MinIOMinIO您的服务器,例如 https://minio.example.com开启路径样式(path-style)请求。服务器必须能通过 HTTPS 从互联网访问。
Hetzner Object Storage自定义 S3 兼容存储https://<location>.your-objectstorage.com
Wasabi自定义 S3 兼容存储https://s3.<region>.wasabisys.com
Vultr Object Storage自定义 S3 兼容存储https://<region>.vultrobjects.com
SumoPod StorageSumoPod Storage从 SumoPod 存储控制台获取默认开启路径样式。
Alibaba Cloud OSSAlibaba Cloud OSShttps://oss-<region>.aliyuncs.com仅支持虚拟主机样式。
Tencent Cloud COSTencent Cloud COShttps://cos.<region>.myqcloud.com存储桶名称包含您的 APPID,例如 my-images-1250000000。
DigitalOcean SpacesDigitalOcean Spaceshttps://<region>.digitaloceanspaces.com
  1. 创建存储桶,以及一个仅限该存储桶、对对象拥有写入、读取和删除权限的密钥(见下文 最小权限)。
  2. 让您的 CDN 可以读取 optimized/,例如使用仅对 optimized/* 允许匿名 s3:GetObject 的存储桶策略。在 MinIO 上:
JSON
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Principal": {        "AWS": [          "*"        ]      },      "Action": [        "s3:GetObject"      ],      "Resource": [        "arn:aws:s3:::my-images/optimized/*"      ]    }  ]}
  1. 在存储桶前面放置 Cloudflare: 为 images.example.com 创建一条开启代理的 DNS 记录,指向存储桶的公共主机。如果服务商要求请求中使用其自身的主机名,请添加一条重写 Host 头的 Cloudflare Origin Rule(对于路径样式的主机,还需在路径中加上存储桶名称)。
  2. 创建 Cloudflare API 令牌,为该区域授予 Zone → Cache Purge → Purge 权限(Zone → Zone → Read 可选)。
  3. 在控制台中: 从表中选择服务商,填写存储桶、Endpoint、区域(服务商没有区域时填 auto)和密钥;在表中注明的情况下开启路径样式请求;然后 CDN 选择 Cloudflare,填写 Zone ID、API 令牌和 CDN 域名。

最小权限

只给 smallPict 能完成必要操作、且仅限一个存储桶的密钥。

服务商权限
Amazon S3对 arn:aws:s3:::<bucket>/* 的 s3:PutObject、s3:GetObject、s3:DeleteObject
Amazon CloudFront对该分配的 cloudfront:CreateInvalidation;可选 cloudfront:GetDistribution,以便自动检测域名
Cloudflare R2具有 Object Read & Write 权限、仅限该存储桶的 R2 API 令牌
Cloudflare(CDN)该区域具有 Zone → Cache Purge → Purge 权限的 API 令牌;可选 Zone → Zone → Read
Google Cloud Storage在该存储桶上拥有 roles/storage.objectUser 的服务账号的 HMAC 密钥
Alibaba Cloud OSS在该存储桶上拥有 oss:PutObject、oss:GetObject、oss:DeleteObject 的 RAM 用户
Tencent Cloud COS在该存储桶上拥有 cos:PutObject、cos:GetObject、cos:DeleteObject 的 CAM 子用户(存储桶名称包含 APPID)
DigitalOcean Spaces仅限该存储桶、具有读取、写入和删除权限的 Spaces 访问密钥
MinIO / 自定义 S3 兼容存储对 <bucket>/* 的写入、读取和删除权限;Endpoint 必须是公网 HTTPS

连接检查

每次保存时(以及点击 重新检查 时),smallPict 都会在使用前检查整个连接:

  1. 存储: 在您存储桶的 .smallpict-probe/ 下写入一个小测试文件,读回后再删除。
  2. Cloudflare: 向您的区域发送一次单个 URL 的测试清除请求。
  3. CloudFront: 为 /.smallpict-probe/... 路径创建一次测试失效(invalidation)。它会计入您当月的 CloudFront 失效路径数。

任何一步失败,都不会切换任何内容。控制台会在相关字段旁显示原因,例如存储桶名称或 API 令牌。连接检查限制为每分钟 5 次。


为 CDN 开放存储桶访问

  • optimized/ 必须能被您的 CDN 读取:可以对该前缀开放公开读取,也可以让 CDN 通过源站访问私有存储桶(CloudFront origin access control,或 R2 自定义域名)。
  • originals/ 可以保持私有。您的 CDN 永远不需要访问它。
  • 只有当浏览器通过 JavaScript 跨源获取图片时(例如 fetch() 或 canvas),才需要 CORS。普通的 <img> 标签不需要。如确有需要,请允许来自您网站源的 GET 和 HEAD 请求。

对象布局与 URL

内容在您存储桶中的键URL
原图originals/<job_id>/<file>不对外分发
优化后的文件optimized/<job_id>.<ext><cdn_domain>/optimized/<job_id>.<ext>

CDN 域名可以包含路径前缀,例如 https://example.com/images;此时 URL 为 https://example.com/images/optimized/<job_id>.<ext>。


缓存头

优化后的文件写入时带有 Cache-Control: public, max-age=31536000, immutable。每个任务都有唯一的键,因此新结果总有新的 URL,很少需要清除缓存。请确保您 CDN 的缓存规则不会用更短的时间覆盖此头。


缓存清除

  • 通过 API 发起的清除请求(POST /v1/purge)会批量发送到 您的 CDN。
  • 在 Cloudflare 上,按 URL 清除文件。“全部清除”只清除您的分发主机和路径前缀,而不是整个区域。
  • 在 CloudFront 上,通过失效(invalidation)清除文件。“全部清除”会创建一个 /* 失效。超出 CloudFront 每月免费额度的失效路径由 AWS 向您的账户计费。

切换模式

切换模式 不会移动现有文件。已分发的文件保留当前 URL;只有新任务会使用新模式。

  • 托管 → BYO: 新任务写入您的存储桶,并由您的 CDN 分发。已在 cdn.smallpict.app 上的文件保留在原处。
  • BYO → 托管: 新任务重新使用托管存储与 CDN。您的 BYO 设置和密钥会继续保存但不使用,因此在您点击 断开连接 之前,无需重新输入即可切换回来。
  • 断开连接: 删除已保存的密钥和 BYO 设置,并将您切回托管模式。您存储桶中的文件不受影响。

出现故障时

  • 如果 smallPict 无法写入您的存储桶,它会重试,然后 将任务标记为失败,并在 API 响应中给出可读的原因。
  • 控制台会显示最近一次错误及其发生时间,并提供 重新检查 按钮。
  • 您会收到 每次事件一封邮件,而不是每个失败任务一封。
  • smallPict 绝不会回退到托管存储。您的文件绝不会存放在您未选择的地方。

安全

  • 密钥保存在 加密的密钥存储 中。之后不会再显示,不会通过 API 返回,也不会写入日志。
  • 要轮换密钥,输入新密钥并保存即可;连接检查会使用新密钥运行。将密钥字段留空则保留已保存的密钥。
  • 存储和 CDN 的 Endpoint 必须使用 HTTPS。解析到私有、回环(loopback)、链路本地(link-local)或其他内部地址的 Endpoint 会被拒绝。
  • 请为 smallPict 使用单独的、权限范围最小的密钥;如果不再使用 BYO,请在服务商控制台中吊销它们。