预签名 URL
公开访问是把整个 Bucket 暴露出去,粒度太粗。有些场景只需要让某个对象在短时间内可访问,比如发一个下载链接给客户,有效期一天就失效。这种临时授权用预签名 URL 最合适。
什么是预签名 URL
预签名 URL 是把 S3 签名直接拼到 URL 查询参数里生成的链接。拿着这个链接的人不需要 Access Key,浏览器直接打开就能下载或上传对象。
它的核心机制是,程序用密钥按签名算法计算出一个签名值,连同过期时间一起放进 URL。R2 收到请求时验签,签名有效且没过期就放行。
| 对比项 | 公开 Bucket | 预签名 URL |
|---|---|---|
| 访问范围 | 整个 Bucket 所有对象 | 单个对象 |
| 有效期 | 永久 | 自定义,可短至几秒 |
| 鉴权方式 | 无 | URL 内嵌签名 |
| 适合场景 | 公开资源站 | 临时下载、客户端直传 |
生成下载链接
用 AWS SDK v3 的 getSignedUrl 方法生成下载链接。先装 presigner
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
生成一个有效期 1 小时的下载链接
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({
region: "auto",
endpoint: "https://你的账户ID.r2.cloudflarestorage.com",
credentials: {
accessKeyId: "你的AccessKeyId",
secretAccessKey: "你的SecretAccessKey",
},
});
const downloadUrl = await getSignedUrl(
s3,
new GetObjectCommand({
Bucket: "my-assets",
Key: "reports/2026-q3.pdf",
}),
{ expiresIn: 3600 } // 单位秒,1 小时
);
console.log(downloadUrl);
// https://xxx.r2.cloudflarestorage.com/my-assets/reports/2026-q3.pdf?X-Amz-...
把这个 URL 发给客户端,浏览器直接打开就能下载 reports/2026-q3.pdf,一小时后自动失效。
生成上传链接
预签名 URL 不仅能下载,还能让客户端直接上传到 R2,文件不经过自己的服务器,省带宽省算力。
生成上传链接用 PutObjectCommand
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const uploadUrl = await getSignedUrl(
s3,
new PutObjectCommand({
Bucket: "my-assets",
Key: "uploads/user-42/avatar.jpg",
ContentType: "image/jpeg",
}),
{ expiresIn: 600 } // 10 分钟
);
前端拿到这个 URL 后用 PUT 方法直接上传文件
const file = fileInput.files[0];
await fetch(uploadUrl, {
method: "PUT",
body: file,
headers: {
"Content-Type": file.type,
},
});
这种模式叫客户端直传,浏览器绕过后端直接把文件写到 R2,后端只负责签发 URL。对大文件、高并发的上传场景非常实用。
Python boto3 生成
Python 端用 boto3 同样能生成预签名 URL
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://你的账户ID.r2.cloudflarestorage.com",
aws_access_key_id="你的AccessKeyId",
aws_secret_access_key="你的SecretAccessKey",
region_name="auto",
)
# 下载链接
download_url = s3.generate_presigned_url(
"get_object",
Params={"Bucket": "my-assets", "Key": "reports/2026-q3.pdf"},
ExpiresIn=3600,
)
# 上传链接
upload_url = s3.generate_presigned_url(
"put_object",
Params={
"Bucket": "my-assets",
"Key": "uploads/user-42/avatar.jpg",
"ContentType": "image/jpeg",
},
ExpiresIn=600,
)
过期时间设计
过期时间 expiresIn 怎么定要分场景
| 场景 | 建议过期时间 | 原因 |
|---|---|---|
| 邮件下载链接 | 1 到 7 天 | 给用户足够时间收到并打开 |
| 网页内嵌图片 | 1 小时 | 页面刷新就重新签发 |
| 客户端上传 | 5 到 15 分钟 | 足够完成一次上传,过期即拒 |
| 临时分享 | 几分钟 | 短时窗口,泄露风险低 |
几个细节要注意。
第一,过期时间是签发时刻算起的,不是从首次访问开始。链接生成后 1 小时失效,不管期间有没有人访问过。
第二,链接一旦签发无法主动吊销。要紧急撤回只能删除对象本身,或轮换 Access Key 让旧签名失效。
第三,预签名 URL 里不含密钥明文,只有签名结果,泄露后到过期前都能用。所以 HTTPS 传输仍然必要,别在日志里记录完整 URL。
小结
预签名 URL 把签名嵌入链接,实现单对象、有时效的临时访问。下载链接用于分发,上传链接用于客户端直传,过期时间按场景定。下一篇讲 R2 和 Workers 的深度集成,在边缘直接读写对象。
上一篇 公开访问与自定义域名
下一篇 与 Workers 绑定集成