AWS无法连接S3存储桶:预签名URL无效及认证方式疑问
问题:AWS认证方式是否正确?构造的S3上传URL无效
后端REST端点代码
import aws from 'aws-sdk' export default async function handler(req, res) { try { // 1. const s3 = new aws.S3({ accessKeyId: process.env.APP_AWS_ACCESS_KEY, secretAccessKey: process.env.APP_AWS_SECRET_KEY, region: process.env.APP_AWS_REGION, }) // 2. aws.config.update({ accessKeyId: process.env.APP_AWS_ACCESS_KEY, secretAccessKey: process.env.APP_AWS_SECRET_KEY, region: process.env.APP_AWS_REGION, signatureVersion: 'v4', }) console.log('bucket: ' + process.env.AWS_S3_BUCKET_NAME) // 3. const post = await s3.createPresignedPost({ Bucket: process.env.AWS_S3_BUCKET_NAME, Fields: { key: req.query.file, }, Expires: 60, // seconds Conditions: [ ['content-length-range', 0, 5048576], // 最大1MB ], }) console.log('post req: ' + JSON.stringify(post)) // 4. return res.status(200).json(post) } catch (error) { console.log(error) } }
构造的无效URL日志
bucket: 'arn:aws:s3:::my-app-bucket' # Will be used in an API route. post req: {"url":"https://s3.eu/'arn:aws:s3:::my-app-bucket' %23 Will be used in an API route."
前端上传代码
const uploadPhoto = async (e) => { const file = e.target.files[0]; const filename = encodeURIComponent(file.name); const res = await fetch(`/api/upload-image?file=${filename}`); const data = await res.json(); const formData = new FormData(); console.log("uploadPhoto()"); console.log("file " + JSON.stringify(file)); console.log("filename " + filename); console.log("data " + JSON.stringify(data)); // @ts-ignore Object.entries({ ...data.fields, file }).forEach(([key, value]) => { formData.append(key as string, value as string); }); toast.promise( fetch(data.url, { method: "POST", body: formData, }), { loading: "Uploading...", success: "Upload successful", error: `Upload failed ${error}`, } ); };
前端日志信息
uploadPhoto() admin.tsx?6c2c:49 file {} admin.tsx?6c2c:50 filename user.png admin.tsx?6c2c:51 data {"url":"https://s3.eu/'arn:aws:s3:::my-app' %23 Will be used in an API route.","fields":{"key":"user.png","bucket":"'arn:aws:s3:::my-app' # Will be used in an API route.","X-Amz-Algorithm":"AWS4-HMAC-SHA256","X-Amz-Credential":"AKIAZMB5ZBK2LSTW23DT/20221025/EU (Frankfurt) eu-central-1/s3/aws4_request","X-Amz-Date":"20221025T133228Z",
问题分析与解决
1. 核心问题:Bucket配置错误
从日志可以明确看到,你的AWS_S3_BUCKET_NAME环境变量值包含了ARN格式、单引号和注释内容,这完全不符合S3 Bucket参数要求——Bucket参数只需要填写纯Bucket名称(比如my-app-bucket),不需要ARN格式,也不能带任何多余符号或注释。
2. 认证方式的优化
你的认证逻辑本身可行,但存在冗余:步骤1已经初始化了带配置的S3实例,步骤2又全局更新aws.config,这两步重复了。可以删掉其中一个,推荐保留全局配置并简化代码:
import aws from 'aws-sdk' export default async function handler(req, res) { try { // 全局配置一次即可 aws.config.update({ accessKeyId: process.env.APP_AWS_ACCESS_KEY, secretAccessKey: process.env.APP_AWS_SECRET_KEY, region: process.env.APP_AWS_REGION, signatureVersion: 'v4', }) const s3 = new aws.S3() // 确保Bucket名称无多余字符 const bucketName = process.env.AWS_S3_BUCKET_NAME.trim() console.log('bucket: ' + bucketName) const post = await s3.createPresignedPost({ Bucket: bucketName, Fields: { key: req.query.file, }, Expires: 60, Conditions: [ ['content-length-range', 0, 5048576], ], }) console.log('post req: ' + JSON.stringify(post)) return res.status(200).json(post) } catch (error) { console.log(error) res.status(500).json({ error: error.message }) // 补充错误响应返回 } }
3. 前端代码修复
你在formData.append时把File对象转成了字符串,会导致文件内容丢失,需单独添加文件对象:
// 修正后的FormData构建逻辑 Object.entries(data.fields).forEach(([key, value]) => { formData.append(key, value); }); formData.append('file', file); // 直接传入File对象,不要转字符串
4. 环境变量修正
将AWS_S3_BUCKET_NAME的值改为纯Bucket名称,例如my-app-bucket,删除所有多余的ARN、引号和注释内容。
内容的提问来源于stack exchange,提问作者Sebi
相关产品推荐
相关产品推荐

