☰
使用 AWS SDK v3 的 `@aws-sdk/s3-presigned-post` 在 JavaScript 中生成 S3 浏览器直传表单(Presigned POST)
2026/10/9 2:27:35 网站建设 项目流程

【免费下载链接】context-hub

项目地址:https://gitcode.com/gh_mirrors/co/context-hub
点击查看免费下载

本篇技术指南以仓库 content/aws/docs/s3-presigned-post/javascript/DOC.md 为主体,系统讲解如何使用 AWS SDK for JavaScript v3 的@aws-sdk/s3-presigned-post包,在受信任的后端生成面向浏览器或其他 multipart 客户端的 S3 HTML 表单上传(Presigned POST)。文章覆盖安装、前置条件、服务端签名、浏览器直传、POST 策略约束、常见陷阱与版本说明,并结合本仓库(Context Hub)中的同源文档与 CLI 用法,帮助读者快速落地一套“后端签名、前端直传、文件不经过应用服务器”的完整上传链路。

包定位:何时该用s3-presigned-post

@aws-sdk/s3-presigned-post解决的核心场景是:后端生成一个 S3 HTML 表单上传,交给浏览器或其他 multipart 客户端直接提交文件。调用createPresignedPost()后,包会返回一个表单url和一组已签名的fields。上传方需要把这些fields与文件一起,以multipart/form-data的 POST 请求发回 S3,S3 校验签名与策略后完成对象写入。

关键区分点在于:如果你的需求是预签名的PUTURL(即把请求本身签名成一个可直接访问的 URL),而不是浏览器表单 POST,那么应该改用@aws-sdk/s3-request-presigner。本仓库也收录了对应文档 content/aws/docs/s3-request-presigner/javascript/DOC.md,其中明确写着“Use@aws-sdk/s3-presigned-postinstead when you need a browser form POST policy rather than a signed request URL”,两份文档互为补充,可按场景二选一。

安装

使用 npm 安装客户端与签名包(同一 AWS SDK v3 应用中建议保持版本一致):

npm install @aws-sdk/client-s3@3.1007.0 @aws-sdk/s3-presigned-post@3.1007.0
  • @aws-sdk/client-s3:提供S3Client实例,承载区域与凭证配置;
  • @aws-sdk/s3-presigned-post:提供核心函数createPresignedPost()。

前置条件

后端凭证环境变量

调用createPresignedPost()的代码只需要正常的 AWS SDK v3 S3 配置即可工作:

AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_SESSION_TOKEN=... AWS_PROFILE=default S3_BUCKET=my-upload-bucket

安全红线:必须在受信任的后端创建 presigned POST,绝不能把长期有效的 AWS 凭证暴露在浏览器代码中。浏览器端只应收到最终的url与fields。

桶的 CORS 规则

浏览器直传场景下,目标桶还需要一条允许前端源发送POST请求的 CORS 规则。以下命令允许http://localhost:3000发起POST并暴露ETag:

aws s3api put-bucket-cors \ --bucket "$S3_BUCKET" \ --cors-configuration '{ "CORSRules": [ { "AllowedOrigins": ["http://localhost:3000"], "AllowedMethods": ["POST"], "AllowedHeaders": ["*"], "ExposeHeaders": ["ETag"] } ] }'

需要注意:合法的签名并不会绕过跨域限制——浏览器上传时桶的 CORS 依然生效,跨域请求必须被 CORS 规则放行。

在服务端创建 presigned POST

初始化S3Client后,调用createPresignedPost()并传入目标桶、对象键以及希望 S3 强制执行的表单约束:

import { S3Client } from "@aws-sdk/client-s3"; import { createPresignedPost } from "@aws-sdk/s3-presigned-post"; const s3 = new S3Client({ region: process.env.AWS_REGION ?? "us-east-1", }); export async function createImageUpload({ key, contentType }) { const bucket = process.env.S3_BUCKET; if (!bucket) { throw new Error("Set S3_BUCKET"); } const { url, fields } = await createPresignedPost(s3, { Bucket: bucket, Key: key, Fields: { "Content-Type": contentType, success_action_status: "201", }, Conditions: [ { "Content-Type": contentType }, { success_action_status: "201" }, ["content-length-range", 0, 5 * 1024 * 1024], ], Expires: 600, }); return { url, fields, key }; }

核心参数说明:

参数作用
Bucket目标桶名称,必填
Key对象键(存储路径),必填;可写成固定路径,也可由调用方传入动态 key
Fields预填进表单的字段,S3 会按这些字段生成签名;浏览器上传时必须原样携带
ConditionsPOST 策略中的约束条件,决定 S3 接受哪些请求;支持精确匹配对象条件与starts-with、content-length-range等数组形式条件
Expirespresigned POST 的有效期,单位是秒

两个容易被忽略的细节:

  1. Expires是 presigned POST 的存活时间(秒),示例中600表示签名在 10 分钟内有效,过期后 S3 会拒绝该表单。
  2. 想让 S3 强制某个字段值(如Content-Type)时,必须同时把它放进Fields和Conditions。只放Fields只是预填默认值,不构成强制约束;只有Conditions里的约束才会写进 POST 策略并被 S3 校验。

从本仓库内容结构看,该文档被组织为aws作者下的多语言变体:content/aws/docs/s3-presigned-post/javascript/DOC.md,其 frontmatter(name: s3-presigned-post、metadata.versions: 3.1007.0、source: maintainer、tags等)遵循仓库 docs/content-guide.md 规定的 DOC.md 规范,供 Agent 通过 CLI 检索定位。

从浏览器上传文件

整体流程:前端把上传意图发给后端 → 后端返回已签名的表单载荷 → 前端把fields与文件组装成FormData,直接 POST 到 S3。

export async function uploadFile(file) { const key = `uploads/${crypto.randomUUID()}-${file.name}`; const presignResponse = await fetch("/api/uploads/presign", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ key, contentType: file.type || "application/octet-stream", }), }); if (!presignResponse.ok) { throw new Error("Failed to create presigned POST"); } const { url, fields } = await presignResponse.json(); const formData = new FormData(); for (const [name, value] of Object.entries(fields)) { formData.append(name, value); } formData.append("file", file); const uploadResponse = await fetch(url, { method: "POST", body: formData, }); if (!uploadResponse.ok) { throw new Error(`S3 upload failed with ${uploadResponse.status}`); } return { key }; }

关键要求:上传方必须把返回的fields原样送回。这些签名值本身就是 POST 策略的一部分,任何缺失、改动或顺序不符都会导致 S3 拒绝请求;文件字段则统一以file为字段名追加到FormData末尾。fetch(url, { method: "POST", body: formData })会自动把请求体编码为multipart/form-data——这也是 S3 表单上传要求的编码格式,该包不生成签名的PUTURL,务必用 POST 提交。

常见策略模式

精确匹配:后端已知字段值

当后端已经知道某个字段的确切取值(例如头像固定为 PNG)时,使用对象形式的精确匹配条件:

const post = await createPresignedPost(s3, { Bucket: process.env.S3_BUCKET, Key: "uploads/avatar.png", Fields: { "Content-Type": "image/png", }, Conditions: [ { "Content-Type": "image/png" }, ["content-length-range", 0, 1 * 1024 * 1024], ], Expires: 300, });

这里{ "Content-Type": "image/png" }强制上传的 Content-Type 必须是image/png,content-length-range把文件大小限制在 1 MiB 以内。

starts-with:一份签名接受多种取值

当需要在同一份签名策略下接受多种取值(例如所有image/*类型)时,使用starts-with条件:

const post = await createPresignedPost(s3, { Bucket: process.env.S3_BUCKET, Key: "uploads/image-upload", Conditions: [ ["starts-with", "$Content-Type", "image/"], ["content-length-range", 0, 10 * 1024 * 1024], ], Expires: 300, });

注意starts-with的写法:第一项是字面量"starts-with",第二项是带$前缀的字段名$Content-Type,第三项是前缀值"image/"。该模式下不需要在Fields里写死具体值,浏览器可自行选择任意image/*类型,文件大小上限放宽到 10 MiB。

重要陷阱清单

  • 签名必须产生于你控制的后端,浏览器只应收到最终的url和fields,杜绝长期凭证暴露。
  • 必须以multipart/form-dataPOST 到返回的url;本包不生成签名的PUTURL(需要 PUT URL 时改用@aws-sdk/s3-request-presigner,见 content/aws/docs/s3-request-presigner/javascript/DOC.md)。
  • 需要 S3 强制表单字段值时,把该值同时放进Fields和Conditions,二者缺一不可。
  • 桶的 CORS 依然生效:合法签名不能绕过跨域限制,浏览器直传必须配好 CORS 规则。
  • S3Client使用的 IAM 凭证仍需具备目标桶与对象键的权限(如s3:PutObject),签名不能越权。
  • 从包根路径导入:import { createPresignedPost } from "@aws-sdk/s3-presigned-post",不要从深层子路径导入。

版本说明与配套

  • 本文档覆盖@aws-sdk/s3-presigned-post版本3.1007.0,与仓库内 content/aws/docs/s3-presigned-post/javascript/DOC.md 的 frontmatter 中versions: "3.1007.0"一致。
  • 需与@aws-sdk/client-s3搭配,二者应保持在同一个 AWS SDK v3 依赖集合中,避免不必要的版本漂移。

在 Context Hub 中获取与定位本文档

本文档是 Context Hub 仓库中按“作者 → 类型 → 条目 → 语言”结构组织的多语言文档变体(结构规则见 docs/content-guide.md)。Agent 或开发者可以通过 CLI 直接检索并拉取该文档:

chub search "s3 presigned post" # 检索相关文档 chub get aws/s3-presigned-post --lang js # 拉取 JavaScript 变体(即本文档)

文档 frontmatter 中的name、description、metadata.tags等字段(见 content/aws/docs/s3-presigned-post/javascript/DOC.md 顶部)正是供chub search检索、供 Agent 快速判断内容与版本匹配度的元数据。围绕本文档,仓库还收录了配套的s3-request-presigner文档(content/aws/docs/s3-request-presigner/javascript/DOC.md),可在需要预签名PUT/GETURL 时对照查阅,形成完整的 S3 签名上传/下载方案。

【免费下载链接】context-hub

项目地址:https://gitcode.com/gh_mirrors/co/context-hub
点击查看免费下载
上一篇:GyroFlow 视频防抖教程:3步用陀螺仪数据稳定抖动视频
下一篇:Karpathy 编码行为指南:四原则实战拆解

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询