使用预签名URL上传文件至AWS S3时遭遇403 Forbidden错误求助
AWS S3预签名URL上传文件出现403 Forbidden问题
我是AWS S3新手,正在搭建一套文件上传流程:用Python生成预签名URL,再通过前端JS借助该URL直接上传文件到S3。使用boto3库生成预签名URL,后端和前端代码如下,选择文件上传时出现"PUT url 403 (Forbidden)"错误,我怀疑问题出在前端PUT请求的代码块中。
后端Python代码
from flask_cors import CORS import boto3 from flask import Flask, request, jsonify app = Flask(__name__) cors = CORS(app) @app.route('/get-signed-url', methods=['POST']) def get_signed_url(): s3 = boto3.client( 's3', aws_access_key_id='***', aws_secret_access_key='***' ) # 获取POST请求中的数据 data = request.get_json() name = data.get('name') # 设置URL参数 params = { 'Bucket': 'myprojecttt', 'Key': name, 'Expires': 30 * 60, } url = s3.generate_presigned_url( ClientMethod='put_object', Params=params ) return jsonify({'url': url}) if __name__ == '__main__': app.run(debug=True)
前端JS代码
<input type="file" id="fileInput"> <button onclick="fetchData()">Upload</button> <script> async function fetchData() { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; console.log(file) if (!file) { alert("Please select a file."); return; } try { const response = await fetch('http://localhost:5000/get-signed-url', { method: 'POST', body: JSON.stringify({ name: file.name }), // 将文件名发送至服务器 headers: { 'Content-Type': 'application/json', // 设置请求内容类型 }, }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); const presignedUrl = data.url; console.log('Presigned URL:', presignedUrl); try { const response = await fetch(presignedUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type, }, }); if (response.ok) { console.log('File uploaded successfully.'); } // else { // console.error('Error:', response.status, response.statusText); // } } catch (error) { console.error('Error:', error); } } catch (error) { console.error('Error:', error); } } </script>
疑似问题代码块
const response = await fetch(presignedUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type, },
可能的原因及解决方法
1. 预签名URL未包含Content-Type参数,签名不匹配
预签名URL的签名会校验请求的Header信息,生成URL时未指定ContentType,但前端上传时添加了Content-Type Header,会触发签名验证失败。
解决方法:
- 前端请求预签名URL时,同时传递文件类型:
body: JSON.stringify({ name: file.name, contentType: file.type }) - 后端生成URL时,在参数中加入
ContentType:# 获取POST请求中的数据 data = request.get_json() name = data.get('name') content_type = data.get('contentType') # 设置URL参数 params = { 'Bucket': 'myprojecttt', 'Key': name, 'Expires': 30 * 60, 'ContentType': content_type # 添加该参数 }
2. IAM用户或Bucket权限不足
确保生成预签名URL的IAM用户拥有s3:PutObject权限,同时Bucket Policy允许该操作。
示例Bucket Policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::你的AWS账号ID:user/你的IAM用户名" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::myprojecttt/*" } ] }
3. 文件名存在编码或特殊字符问题
如果文件名包含特殊字符,可能导致生成URL时的Key和实际请求的Key不匹配。可以对文件名进行URL编码后再传递给后端生成签名。
4. 前端请求存在多余Header
确保前端PUT请求的Header和预签名URL生成时指定的参数完全一致,不要添加额外的Header(比如X-Requested-With等),否则会破坏签名验证。
内容的提问来源于stack exchange,提问作者Quốc Khánh Nguyễn
相关产品推荐
相关产品推荐

