Node.js使用预签名URL传视频至AWS S3遇501 Not Implemented错误
尝试通过预签名URL将视频文件上传至AWS S3,但收到status: 501, statusText: 'Not Implemented'错误。已参考相关问题尝试设置Content-Type和Content-Length,但问题依旧。
生成预签名URL的代码
var S3 = require('aws-sdk/clients/s3') async function getS3UploadSignedUrl() { var s3 = new S3({ accessKeyId: config.aws.accessKeyId, secretAccessKey: config.aws.secretAccessKey, region: config.aws.region, apiVersion: '2010-12-01' }); let params = { Bucket: config.aws.s3Bucket, Key: 'myVideo.mp4', Expires: 60 * 60, // 单位:秒 ContentType: 'video/mp4', ACL: 'public-read', } let signedUrl = await s3.getSignedUrl('putObject', params); console.log('signedUrl', signedUrl) }
上传代码
const fs = require('fs'); const FILE_PATH = '/home/alok/Downloads/video.mp4'; const signedUrl = 'https://my pre signed url' const fileStream = fs.createReadStream(FILE_PATH); const contentLength = fs.statSync(FILE_PATH).size; async function uploader() { var options = { method: "PUT", headers: { "content-type": "video/mp4" }, body: fileStream, 'Content-Length': contentLength } let response = await fetch(signedUrl, options) console.log(response) } uploader()
存储桶策略
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicRead", "Effect": "Allow", "Principal": "*", "Action": [ "s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::my-bucket/*" } ] }
CORS配置
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "HEAD", "PUT", "POST" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [ "x-amz-server-side-encryption", "x-amz-request-id", "x-amz-id-2" ], "MaxAgeSeconds": 3000 } ]
请问遗漏了什么配置或步骤,导致无法完成视频上传?
1. 修正上传请求的Content-Length位置
上传代码中Content-Length被放在options根层级,这是错误的,它属于HTTP请求头,必须放在headers对象内,否则不会被识别为请求头发送。
修改后的上传代码:
async function uploader() { var options = { method: "PUT", headers: { "content-type": "video/mp4", "Content-Length": contentLength // 移至headers中 }, body: fileStream } let response = await fetch(signedUrl, options) console.log(response) }
2. 修正预签名URL生成逻辑
AWS SDK v2的getSignedUrl是同步方法,不需要加await,错误使用await可能导致生成无效URL。调整代码:
let signedUrl = s3.getSignedUrl('putObject', params);
3. 确认预签名URL使用正确的S3端点
如果预签名URL使用了存储桶的静态网站托管域名(如http://my-bucket.s3-website-us-east-1.amazonaws.com),会返回501错误——静态网站模式仅支持GET/HEAD请求。必须使用S3 REST API域名(如https://my-bucket.s3.us-east-1.amazonaws.com)生成预签名URL。
4. 确保请求头与预签名参数完全匹配
预签名时指定了ContentType: 'video/mp4',上传时的content-type头需保持一致(HTTP头不区分大小写,但建议完全匹配)。同时不要添加预签名参数中未包含的额外请求头,否则会触发签名验证失败。
5. 检查生成签名的IAM用户权限
生成预签名URL的IAM用户需要具备s3:PutObject和s3:PutObjectAcl权限(因为指定了ACL: public-read),需确保用户的IAM策略包含这些权限,而非仅依赖存储桶策略。
示例IAM策略:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::my-bucket/*" } ] }
内容的提问来源于stack exchange,提问作者Alok

