首页 > 解决方案 > dropzone.js 上传到 Amazon S3 为第 3 次以上上传产生 400(错误请求)

问题描述

我正在尝试实现一个 dropzone.js 接口来将文件上传到 Amazon S3。我遵循了我在网上找到的几个例子

<html>
<head>
    <title>S3 Dropzone</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link rel="stylesheet" href="https://rawgit.com/enyo/dropzone/master/dist/dropzone.css">
    <script src="https://rawgit.com/enyo/dropzone/master/dist/dropzone.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.476.0.min.js"></script>
    <script language="JavaScript">
        var bucket = "test-bucket";
        var region = "us-east-2";
        var userId = "11111";

        $(document).ready(function($) {
            $.ajax({
                url: "/s3_credentials.php",
                data: {},
                type: 'POST',
                dataType: 'json',
                success: function(response) {
                    bucket = response.Bucket;
                    region = response.region;
                    AWS.config.credentials = new AWS.Credentials({
                        accessKeyId:response.accessKeyId,
                        secretAccessKey:response.secretAccessKey
                    });
                    console.log("credentials response", response, AWS.config.credentials );
                }
            })
        });
        // let aws-sdk send events to dropzone.
        function sendEvents(file) {
            let progress = i => dz.emit('uploadprogress', file, i.loaded * 100 / i.total, i.loaded);
            file.s3upload.on('httpUploadProgress', progress);
            file.s3upload.send(err => err ? dz.emit('error', file, err) : dz.emit('complete', file));
        }

        Dropzone.prototype.uploadFiles = files => files.map(sendEvents);
    </script>

</head>

<body>

<br><br><br>

<h1 style="text-align: center">ImageUpload to S3Bucket</h1>
<div id="dz" class="dz-clickable dropzone"></div>

<script language="JavaScript">
    Dropzone.autoDiscover = false;
    let canceled = file => { if (file.s3upload) file.s3upload.abort() }
    let options = {
        url: '/',
        canceled,
        removedfile: canceled,
        accept(file, done) {
            let params = {
                Bucket: bucket,
                Key: userId + "/" + file.name,
                Body: file
            };
            params.accessKeyId = AWS.config.credentials.accessKeyId;
            params.secretAccessKey = AWS.config.credentials.secretAccessKey;
            console.log('upload params', params);
            file.s3upload = new AWS.S3.ManagedUpload({params});
            done();
        }
    };
    var dz = new Dropzone('#dz', options );
</script>
</body>
</html>

凭据脚本很简单:

<?php
$AWSBucket = "dropzone-bucket-test";
$AWSRegion = "us-east-2";
$AWSUserArn = "arn:aws:iam::22222222222:user/test-bucket";
$AWSUserAccessKeyId = "*****************";
$AWSUserSecretAccessKey = "**********************************";

header('Content-Type: application/json');
echo json_encode([
    'Bucket'          => $AWSBucket,
    'accessKeyId'     => $AWSUserAccessKeyId,
    'secretAccessKey' => $AWSUserSecretAccessKey,
    'region'          => $AWSRegion,
    'userArn'         => $AWSUserArn
]);

第 3 次以上 PUT 调用的回复似乎表明该地区正在以某种方式回落到某处的默认值 us-east-1。我的代码都不包含该区域信息,但我在某些 AWS sdk 中将其视为默认值。

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>AuthorizationHeaderMalformed</Code>
    <Message>The authorization header is malformed; the region 'us-east-1' is wrong; expecting 'us-east-2'</Message>
    <Region>us-east-2</Region>
    <RequestId>********************</RequestId>
    <HostId>***********************************************************************=</HostId>
</Error>

它将毫无问题地上传1或2个文件。之后,它似乎开始失败并出现 400 个错误。检查回复,它说该地区是错误的,但我不确定它是如何变成这样的。我假设它回退到 aws-sdk 中的默认值,但我不知道为什么。

在进行凭据调用以从服务器获取配置的值之前,我什至尝试使用正确的值初始化我的变量。

任何帮助表示赞赏。

标签: amazon-web-servicesamazon-s3aws-sdkdropzone.js

解决方案


我不确定您的代码为什么会显示这种行为,但对我来说似乎很奇怪的一件事是第一个脚本的结构。您正在$(document).ready通过异步 AJAX 调用在函数中获取凭证,但负责的代码// let aws-sdk send events to dropzone.位于 AJAX 的成功处理程序之外,甚至在$(document).ready函数之外,这意味着它在您的 AWS 开发工具包配置这些凭证之前执行。

我建议将上述代码放入 AJAX 的成功处理程序中。

<script language="JavaScript">
    var bucket = "test-bucket";
    var region = "us-east-2";
    var userId = "11111";

    function sendEvents(file) {
        let progress = i => dz.emit('uploadprogress', file, i.loaded * 100 / i.total, i.loaded);
        file.s3upload.on('httpUploadProgress', progress);
        file.s3upload.send(err => err ? dz.emit('error', file, err) : dz.emit('complete', file));
    }

    $(document).ready(function($) {
        $.ajax({
            url: "/s3_credentials.php",
            data: {},
            type: 'POST',
            dataType: 'json',
            success: function(response) {
                bucket = response.Bucket;
                region = response.region;
                AWS.config.credentials = new AWS.Credentials({
                    accessKeyId:response.accessKeyId,
                    secretAccessKey:response.secretAccessKey
                });
                console.log("credentials response", response, AWS.config.credentials);

                // let aws-sdk send events to dropzone.
                Dropzone.prototype.uploadFiles = files => files.map(sendEvents);
            }
        })
    });
</script>

推荐阅读