amazon-web-services - 如何通过boto设置s3浏览器上传的内容长度范围

标签 amazon-web-services amazon-s3 reactjs boto boto3

问题

我正在尝试从浏览器将图像直接上传到 S3,但在通过 boto 的 S3Connection.generate_url 方法应用内容长度范围权限时遇到了困难。

有大量关于 signing POST forms 的信息, setting policies in general甚至是heroku method for doing a similar submission 。我一生都无法弄清楚如何将“内容长度范围”添加到签名网址中。

使用 boto 的generate_url 方法(下面的示例),我可以指定策略 header 并使其适用于正常上传。我似乎无法添加对最大文件大小的策略限制。

服务器签名代码

## django request handler 
from boto.s3.connection import S3Connection
from django.conf import settings
from django.http import HttpResponse
import mimetypes
import json

conn = S3Connection(settings.S3_ACCESS_KEY, settings.S3_SECRET_KEY)
object_name = request.GET['objectName']
content_type = mimetypes.guess_type(object_name)[0]

signed_url = conn.generate_url(
    expires_in = 300, 
    method = "PUT", 
    bucket = settings.BUCKET_NAME, 
    key = object_name,
    headers = {'Content-Type': content_type, 'x-amz-acl':'public-read'})

return HttpResponse(json.dumps({'signedUrl': signed_url}))

在客户端,我使用 ReactS3Uploader这是基于 tadruj's s3upload.js script 。它不应该影响任何东西,因为它似乎只是传递签名Urls 涵盖的任何内容,但为了简单起见复制在下面。

ReactS3Uploader JS 代码(简化)

uploadFile: function() {
    new S3Upload({
        fileElement: this.getDOMNode(),
        signingUrl: /api/get_signing_url/,
        onProgress: this.props.onProgress,
        onFinishS3Put: this.props.onFinish,
        onError: this.props.onError
    });
},

render: function() {
    return this.transferPropsTo(
        React.DOM.input({type: 'file', onChange: this.uploadFile})
    );
}

S3upload.js

S3Upload.prototype.signingUrl = '/sign-s3';
S3Upload.prototype.fileElement = null;

S3Upload.prototype.onFinishS3Put = function(signResult) {
    return console.log('base.onFinishS3Put()', signResult.publicUrl);
};

S3Upload.prototype.onProgress = function(percent, status) {
    return console.log('base.onProgress()', percent, status);
};

S3Upload.prototype.onError = function(status) {
    return console.log('base.onError()', status);
};

function S3Upload(options) {
    if (options == null) {
        options = {};
    }
    for (option in options) {
        if (options.hasOwnProperty(option)) {
            this[option] = options[option];
        }
    }
    this.handleFileSelect(this.fileElement);
}

S3Upload.prototype.handleFileSelect = function(fileElement) {
    this.onProgress(0, 'Upload started.');
    var files = fileElement.files;
    var result = [];
    for (var i=0; i < files.length; i++) {
        var f = files[i];
        result.push(this.uploadFile(f));
    }
    return result;
};

S3Upload.prototype.createCORSRequest = function(method, url) {
    var xhr = new XMLHttpRequest();

    if (xhr.withCredentials != null) {
        xhr.open(method, url, true);
    }
    else if (typeof XDomainRequest !== "undefined") {
        xhr = new XDomainRequest();
        xhr.open(method, url);
    }
    else {
        xhr = null;
    }
    return xhr;
};

S3Upload.prototype.executeOnSignedUrl = function(file, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', this.signingUrl + '&objectName=' + file.name, true);
    xhr.overrideMimeType && xhr.overrideMimeType('text/plain; charset=x-user-defined');
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            var result;
            try {
                result = JSON.parse(xhr.responseText);
            } catch (error) {
                this.onError('Invalid signing server response JSON: ' + xhr.responseText);
                return false;
            }
            return callback(result);
        } else if (xhr.readyState === 4 && xhr.status !== 200) {
            return this.onError('Could not contact request signing server. Status = ' + xhr.status);
        }
    }.bind(this);
    return xhr.send();
};

S3Upload.prototype.uploadToS3 = function(file, signResult) {
    var xhr = this.createCORSRequest('PUT', signResult.signedUrl);
    if (!xhr) {
        this.onError('CORS not supported');
    } else {
        xhr.onload = function() {
            if (xhr.status === 200) {
                this.onProgress(100, 'Upload completed.');
                return this.onFinishS3Put(signResult);
            } else {
                return this.onError('Upload error: ' + xhr.status);
            }
        }.bind(this);
        xhr.onerror = function() {
            return this.onError('XHR error.');
        }.bind(this);
        xhr.upload.onprogress = function(e) {
            var percentLoaded;
            if (e.lengthComputable) {
                percentLoaded = Math.round((e.loaded / e.total) * 100);
                return this.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing.' : 'Uploading.');
            }
        }.bind(this);
    }
    xhr.setRequestHeader('Content-Type', file.type);
    xhr.setRequestHeader('x-amz-acl', 'public-read');
    return xhr.send(file);
};

S3Upload.prototype.uploadFile = function(file) {
    return this.executeOnSignedUrl(file, function(signResult) {
        return this.uploadToS3(file, signResult);
    }.bind(this));
};


module.exports = S3Upload;

如果我能提供任何帮助,我将不胜感激,因为我已经用头撞墙好几个小时了。

最佳答案

您无法将其添加到签名的 PUT URL 中。这只适用于与 POST 一起签名的策略,因为这两种机制非常不同。

对 URL 进行签名是一个有损的过程(因为没有更好的术语)。您生成要签名的字符串,然后对其进行签名。您随请求发送签名,但丢弃并且不发送要签名的字符串。然后,S3 会针对收到的请求重建要签名的字符串,并生成您应随该请求发送的签名。只有一个正确答案,并且 S3 不知道您实际签署的字符串是什么。签名匹配或不匹配,要么是因为您构建的字符串签名不正确,要么是您的凭据不匹配,并且它不知道是哪种可能性。它只知道,根据您发送的请求,您应该签名的字符串以及签名应该是什么

考虑到这一点,要让 content-length-range 使用签名 URL,客户端需要实际发送这样的请求头......这不会产生很有道理。

相反,通过 POST 上传,可以向 S3 传达更多信息。它不仅涉及您的签名是否有效,还涉及您的政策文件……因此可以在请求中包含指令(政策)。它们受到签名的保护,不会被更改,但它们不会被加密或散列——整个策略可由 S3 读取(因此,相比之下,我们将其称为相反的“无损”。)

这种差异就是为什么您无法使用 PUT 执行您想要执行的操作,而使用 POST 可以执行的操作。

关于amazon-web-services - 如何通过boto设置s3浏览器上传的内容长度范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29550537/

相关文章:

pandas - 从 S3 读取 Parquet 的最快方法

java - 如何为此单元测试正确模拟 AWS?

javascript - react : setting scrollTop property of div doesn't work

javascript - react : How to prompt for unsaved changes when navigating with react-router-component

python - AWS Lambda RDS MySQL 数据库连接接口(interface)错误

amazon-web-services - 如何将 Amazon S3 对象移动到分区目录中

amazon-web-services - 为什么不同运行时的 AWS lambda 计费不同?

java - spring cloud aws 项目不再有活跃的所有者了吗?

java - 无法在 Eclipse 中使用 localhost 将文件上传到 amazon S3 存储桶

reactjs - Redux 形式,提供给 'value' 的类型 'number' 的无效 prop 'TextInput' ,预期为 'string'