使用分段上传将文件放入 Amazon S3

发布于 2024-12-28 17:18:24 字数 2666 浏览 1 评论 0原文

我正在尝试使用 Amazon Java SDK 通过分段上传来上传文件。这个想法是将上传 ID 传递给小程序,小程序将文件部分放入只读存储桶中。通过这种方式,我避免将 AWS 凭证存储在小程序中。

在我的测试中,我使用 boto (python) 生成一个 upload-id 并将文件存储到存储桶中。效果很好。

我的 Applet 从 S3 收到“403 访问被拒绝”,我不知道为什么。

这是我的代码(部分取自 http://docs.amazonwebservices.com/AmazonS3/latest/dev /llJavaUploadFile.html):

AmazonS3 s3Client = new AmazonS3Client();
List<PartETag> partETags = new ArrayList<PartETag>();

long contentLength = file.length();
long partSize = Config.getInstance().getInt("part_size");
String bucketName = Config.getInstance().getString("bucket");
String keyName = "mykey";
String uploadId = getParameter("upload_id");

try {
    long filePosition = 0;
    for (int i = 1; filePosition < contentLength; i++) {

        partSize = Math.min(partSize, (contentLength - filePosition));

        // Create request to upload a part.
        UploadPartRequest uploadRequest = new UploadPartRequest()
            .withBucketName(bucket).withKey(keyName)
            .withUploadId(uploadId).withPartNumber(i)
            .withFileOffset(filePosition)
            .withFile(file)
            .withPartSize(partSize);

        // Upload part and add response to our list.
        partETags.add(s3Client.uploadPart(uploadRequest).getPartETag());

        filePosition += partSize;
    }

    System.out.println("Completing upload");
    CompleteMultipartUploadRequest compRequest = new 
                CompleteMultipartUploadRequest(bucket, 
                                            keyName, 
                                            uploadId, 
                                            partETags);

    s3Client.completeMultipartUpload(compRequest);
} catch (Exception e) {
    s3Client.abortMultipartUpload(new AbortMultipartUploadRequest(
            bucketName, keyName, uploadId));
}

在小程序调试日志中,我发现了这一点,那么:

INFO: Sending Request: PUT https://mybucket.s3.amazonaws.com /mykey Parameters: (uploadId: V4hwobOLQ1rYof54zRW0pfk2EfhN7B0fpMJTOpHOcmaUl8k_ejSo_znPI540.lpO.ZO.bGjh.3cx8a12ZMODfA--, partNumber: 1, ) Headers: (Content-Length: 4288546, Content-Type: application/x-www-form-urlencoded; charset=utf-8, ) 
24.01.2012 16:48:42 com.amazonaws.http.AmazonHttpClient handleErrorResponse
INFO: Received error response: Status Code: 403, AWS Service: null, AWS Request ID: DECF32CCFEE9EBF0, AWS Error Code: AccessDenied, AWS Error Message: Access Denied, S3 Extended Request ID: xtL1ixsGM2/vsxJ+cZRHpkPZ23SMfP8hZZjQCQnp8oWGwdS2/aGfYgomihyqaDCQ

您在代码中发现任何明显的失败吗?

谢谢, 斯特凡

I'm trying to upload a file with the Amazon Java SDK, via multipart upload. The idea is to pass an upload-id to an applet, which puts the file parts into a readonly-bucket. Going this way, I avoid to store AWS credentials in the applet.

In my tests, I generate an upload-id with boto (python) and store a file into the bucket. That works well.

My Applet gets a "403 Access denied" from the S3, and I have no idea why.

Here's my code (which is partially taken from http://docs.amazonwebservices.com/AmazonS3/latest/dev/llJavaUploadFile.html):

AmazonS3 s3Client = new AmazonS3Client();
List<PartETag> partETags = new ArrayList<PartETag>();

long contentLength = file.length();
long partSize = Config.getInstance().getInt("part_size");
String bucketName = Config.getInstance().getString("bucket");
String keyName = "mykey";
String uploadId = getParameter("upload_id");

try {
    long filePosition = 0;
    for (int i = 1; filePosition < contentLength; i++) {

        partSize = Math.min(partSize, (contentLength - filePosition));

        // Create request to upload a part.
        UploadPartRequest uploadRequest = new UploadPartRequest()
            .withBucketName(bucket).withKey(keyName)
            .withUploadId(uploadId).withPartNumber(i)
            .withFileOffset(filePosition)
            .withFile(file)
            .withPartSize(partSize);

        // Upload part and add response to our list.
        partETags.add(s3Client.uploadPart(uploadRequest).getPartETag());

        filePosition += partSize;
    }

    System.out.println("Completing upload");
    CompleteMultipartUploadRequest compRequest = new 
                CompleteMultipartUploadRequest(bucket, 
                                            keyName, 
                                            uploadId, 
                                            partETags);

    s3Client.completeMultipartUpload(compRequest);
} catch (Exception e) {
    s3Client.abortMultipartUpload(new AbortMultipartUploadRequest(
            bucketName, keyName, uploadId));
}

In the applet debug log, I find this, then:

INFO: Sending Request: PUT https://mybucket.s3.amazonaws.com /mykey Parameters: (uploadId: V4hwobOLQ1rYof54zRW0pfk2EfhN7B0fpMJTOpHOcmaUl8k_ejSo_znPI540.lpO.ZO.bGjh.3cx8a12ZMODfA--, partNumber: 1, ) Headers: (Content-Length: 4288546, Content-Type: application/x-www-form-urlencoded; charset=utf-8, ) 
24.01.2012 16:48:42 com.amazonaws.http.AmazonHttpClient handleErrorResponse
INFO: Received error response: Status Code: 403, AWS Service: null, AWS Request ID: DECF32CCFEE9EBF0, AWS Error Code: AccessDenied, AWS Error Message: Access Denied, S3 Extended Request ID: xtL1ixsGM2/vsxJ+cZRHpkPZ23SMfP8hZZjQCQnp8oWGwdS2/aGfYgomihyqaDCQ

Do you find any obvious failures in the code?

Thanks,
Stefan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

π浅易 2025-01-04 17:18:24

虽然您的用例是合理的并且这确实是一个明显的尝试,但我不认为 Multipart Upload API 的设计目的就是允许这样做,而您实际上违反了安全屏障:

上传 ID 只是一个标识符,用于协助 Multipart Upload API 将各部分组装在一起(即更像是一个临时对象密钥)不是专用的安全机制(见下文)。因此,您仍然需要适当的访问凭证,但由于您正在调用 AmazonS3Client(),它构建一个新的 Amazon S3 客户端,该客户端将向 Amazon S3 发出匿名请求,您的请求会相应地产生 403 访问被拒绝

您可以通过 使用预签名 URL 上传对象,不幸的是,尽管没有多部分功能:

预签名 URL 使您可以访问 URL 中标识的对象,
前提是预签名 URL 的创建者有权
访问该对象。也就是说,如果您收到要上传的预签名 URL
一个对象,只有该对象的创建者才可以上传该对象
预签名 URL 具有上传该对象所需的权限。

[...] 预签名 URL
如果您希望您的用户/客户能够上传特定的
对象 [...],但您不要求它们具有 AWS 安全性
凭据或权限。创建预签名 URL 时,您必须
提供您的安全凭证,指定存储桶名称和对象
key、HTTP 方法(上传对象的 PUT)和过期日期
和时间。 [...]

冗长的引用说明了为什么像这样的系统可能需要更复杂的安全设计,而不是“仅仅”分发上传ID(乍一看两者很相似)。

显然,人们希望能够同时使用这两种功能,但这似乎尚不可用。

While your use case is sound and this is an obvious attempt indeed, I don't think the Multipart Upload API has been designed to allow this and you are actually violating a security barrier:

The upload ID is merely an identifier to assist the Multipart Upload API in assembling the parts together (i.e. more like a temporary object key) not a dedicated security mechanism (see below). Consequently you still require proper access credentials in place, but since you are calling AmazonS3Client(), which Constructs a new Amazon S3 client that will make anonymous requests to Amazon S3, your request yields a 403 Access denied accordingly.

What you are trying to achieve is possible via Uploading Objects Using Pre-Signed URLs, albeit only without the multipart functionality, unfortunately:

A pre-signed URL gives you access to the object identified in the URL,
provided that the creator of the pre-signed URL has permissions to
access that object. That is, if you receive a pre-signed URL to upload
an object, you can upload the object only if the creator of the
pre-signed URL has the necessary permissions to upload that object.

[...] The pre-signed URLs
are useful if you want your user/customer to be able upload a specific
object [...], but you don't require them to have AWS security
credentials or permissions. When you create a pre-signed URL, you must
provide your security credentials, specify a bucket name an object
key, an HTTP method (PUT of uploading objects) and an expiration date
and time. [...]

The lenghty quote illustrates, why a system like this likely needs a more complex security design than 'just' handing out an upload ID (as similar as both might appear at first sight).

Obviously one would like to be able to use both features together, but this doesn't appear to be available yet.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文