You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为S3预签名URL(PUT而非POST)添加元数据-JavaScript SDK V3

S3预签名PUT URL添加元数据触发403禁止错误的解决方案

问题核心

预签名URL生成后查询字符串包含元数据,但PUT请求返回403,核心原因是元数据的签名匹配逻辑不严谨,涉及键名大小写、参数重复定义、编码一致性三个问题。

具体问题与修复方案

1. 键名大小写不匹配

服务端签名头部用的是x-amz-meta-fileSize(首字母大写),但客户端请求头是x-amz-meta-filesize(全小写)。S3对自定义元数据的签名是严格区分大小写的,必须保证两端完全一致。

2. Metadata参数与签名头部重复定义

在PutObjectCommand的params.Metadata中定义元数据时,S3会自动生成对应的x-amz-meta-*请求头并加入签名逻辑。如果同时在signedHeaders中指定这些头部,会导致签名冲突——要么通过Metadata传递(无需额外签名头部),要么通过请求头传递(不在Metadata中定义)。

3. 编码重复处理

服务端和客户端都对origfilename执行了encodeURIComponent,导致双重编码,签名时内容不匹配。


修正后的代码示例

方案一:通过请求头传递元数据并签名(推荐灵活场景)

Lambda生成URL代码

const params={
    Bucket: bucket,
    Key: key,
    // 移除Metadata,改为客户端通过请求头传递
};
console.log("params=", params);

// 统一使用全小写键名,和客户端请求头保持一致
const headersToSign= new Set([
    "x-amz-meta-filesize",
    "x-amz-meta-filetype",
    "x-amz-meta-origfilename",
]);

const command=new PutObjectCommand(params);

const url = await getSignedUrl(s3Client, command, { 
    expiresIn: expiry,
    signedHeaders: headersToSign,
});

客户端fetch代码

const encodedFileName = encodeURIComponent(fileToSubmit.name);

const myHeaders = new Headers({
    "x-amz-meta-filesize": fileToSubmit.size.toString(), // 确保为字符串类型
    "x-amz-meta-filetype": fileToSubmit.type,
    "x-amz-meta-origfilename": encodedFileName,
});

const myRequest = new Request(url, {
    method: 'PUT',
    mode: 'cors',
    cache: 'default',
    body: fileToSubmit, 
    headers: myHeaders
});

fetch(myRequest)
.then(response => {
    if (!response.ok) throw new Error(`上传失败:HTTP状态码 ${response.status}`);
    console.log("文件上传成功");
})
.catch(err => console.error("上传异常:", err));

方案二:通过服务端Metadata定义元数据(适合固定元数据场景)

Lambda生成URL代码

const params={
    Bucket: bucket,
    Key: key,
    Metadata:{
        "filesize": filesInfo.fileSize.toString(),
        "origfilename": filesInfo.origFilename, // 服务端无需编码
        "filetype": filesInfo.fileType,
    },
};

const command=new PutObjectCommand(params);

const url = await getSignedUrl(s3Client, command, { 
    expiresIn: expiry,
    // 无需指定signedHeaders,Metadata会自动纳入签名
});

客户端fetch代码

const myRequest = new Request(url, {
    method: 'PUT',
    mode: 'cors',
    cache: 'default',
    body: fileToSubmit, 
    // 无需传递x-amz-meta-*请求头
});

fetch(myRequest)
.then(response => {
    if (!response.ok) throw new Error(`上传失败:HTTP状态码 ${response.status}`);
    console.log("文件上传成功");
})
.catch(err => console.error("上传异常:", err));

内容的提问来源于stack exchange,提问作者robert smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 05:35:17