微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

尝试使用 Boto3Django + Javascript通过预先签名的帖子将文件上传到 AWS S3 时被禁止获取 403

如何解决尝试使用 Boto3Django + Javascript通过预先签名的帖子将文件上传到 AWS S3 时被禁止获取 403

我已经尝试在 SO 和其他论坛上研究其他主题,但仍然无法解决这个问题。我正在向 S3 生成一个预先签名的帖子,并尝试使用这些标头将文件上传到它,但收到 403: Forbidden。

权限 加载了 Boto3 的 IAM 用户具有列出、读取和写入 S3 的权限。

IAM permissions

CORS 允许来自所有来源和所有标头的 CORS

[
{
    "AllowedHeaders": [
        "*"
    ],"AllowedMethods": [
        "GET","HEAD","POST","PUT"
    ],"AllowedOrigins": [
        "*"
    ],"ExposeHeaders": []
}
]

代码代码基于 Django 中的 Python 以及 Javascript。这是逻辑:

首先从 HTML 中检索文件,并用于调用检索签名 URL 的函数

(function () {
    document.getElementById("file-input").onchange = function () {
        let files = document.getElementById("file-input").files;
        let file = files[0];
        Object.defineProperty(file,"name",{
            writeable: true,value: `${uuidv4()}.pdf`

        })
        if (!file) {
            return alert("No file selected");
        }
        getSignedRequest(file);
    }
})();

然后使用 Django 视图发送 GET 请求以检索签名 URL(在此视图之后的下一节中描述)

function getSignedRequest(file) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET","/sign_s3?file_name=" + file.name + "&file_type=" + file.type)
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                let response = JSON.parse(xhr.responseText)
                uploadFile(file,response.data,response.url)
            }
            else {
                alert("Could not get signed URL")
            }
        }
    };
    xhr.send()
}

生成签名 URL 的 Django 视图

def Sign_s3(request):

    S3_BUCKET = os.environ.get("BUCKET_NAME")

    if (request.method == "GET"):
        file_name = request.GET.get('file_name')
        file_type = request.GET.get('file_type')
        
        s3 = boto3.client('s3',config = boto3.session.Config(signature_version = 's3v4'))
        
        presigned_post = s3.generate_presigned_post(
        Bucket = S3_BUCKET,Key = file_name,Fields = {"acl": "public-read","Content-Type": file_type},Conditions = [
        {"acl": "public-read"},{"Content-Type": file_type}
        ],ExpiresIn = 3600
    )


    return JsonResponse({
        "data": presigned_post,"url": "https://%s.s3.amazonaws.com/%s" % (S3_BUCKET,file_name)
    })

最后应该将文件上传到存储桶(这是我收到 403 错误的地方)

function uploadFile(file,s3Data,url) {
            let xhr = new XMLHttpRequest();
            xhr.open("POST",s3Data.url)

            let postData = new FormData()
            for (key in s3Data.fields) {
                postData.append(key,s3Data.fields[key])
            }

            postData.append("file",file)

            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200 || xhr.status === 204) {
                        document.getElementById("cv-url").value = url
                    }
                    else {
                        alert("Could not upload file")
                    }
                }
            };
            xhr.send(postData)
        }

网络请求 这是网络请求在浏览器中的样子

Network request 1

Network request 2

Network request 3

解决方法

您应该在上传中使用的 URL 应该是预签名响应所具有的 URL。不要只是上传你想要的任何网址。

将您的回复更新为:

return JsonResponse({
  "data": presigned_post,"url": presigned_post.url
})

具体来说,您使用的网址如下所示:

https://BUCKTET_NAME.s3.amazonaws.com/KEY_PATH

什么时候应该是这样:

https://s3.REGION.amazonaws.com/BUCKET_NAME

然而,查看您的代码这是它应该做的,但检查员的屏幕截图却另有说明。为什么网络请求中的 url 与 create_presigned_post 请求返回的 url 不匹配?

,

@jellycsc 帮助了我。我必须打开存储桶的 BlockPublicAcl 选项才能使其工作。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。