使用分块上传将文件上传到Amazon S3

6
我正在尝试使用Amazon Java SDK通过多部分上传来上传文件。我的想法是将upload-id传递给小程序,然后将文件部分放入只读存储桶。通过这种方式,我避免在小程序中存储AWS凭据。

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

我的小程序从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

你在代码中发现了明显的错误吗?
谢谢, Stefan
1个回答

7
虽然您的用例是有理由的,这确实是一个明显的尝试,但我认为Multipart Upload API并没有被设计允许这样做,而且您实际上正在违反安全障碍:
上传ID只是一个帮助“多部分上传API”将部分组装在一起的标识符(即更像是临时对象键)而不是专用的安全机制(见下文)。因此,您仍然需要正确的访问凭据,但由于您正在调用AmazonS3Client(),它“构造一个新的 Amazon S3 客户端,该客户端将对 Amazon S3 进行匿名请求”,因此您的请求相应地产生了“403 拒绝访问”的错误。
虽然只能在没有多部分功能的情况下使用使用预签名 URL 上传对象来实现您要达到的目标:

预签名 URL 使您可以访问在 URL 中标识的对象,前提是预签名 URL 的创建者有权限访问该对象。换句话说,如果您收到一个预签名 URL 来上传一个对象,只有创建预签名 URL 的人具有上传该对象所必需的权限,您才能上传该对象。
[...] 如果您希望您的用户/客户能够上传特定对象 [...]
但您不要求他们拥有 AWS 安全凭据或权限,则预签名 URL 非常有用。当您创建预签名 URL 时,必须提供您的安全凭据,指定存储桶名称和对象键,HTTP 方法(上传对象的 PUT)以及过期日期和时间。 [...]

上述引用说明了为什么这样的系统“可能”需要比“只”分配上传ID更复杂的安全设计(尽管两者在第一眼看起来很相似)。
显然,人们希望能够同时使用两个功能,但目前似乎还无法实现。

1
非常感谢您的想法。即使可以对公开可用的存储桶执行匿名请求,但您的假设似乎是正确的,即分段上传不适用于我的情况。而且,预签名URL不适用于分段上传也太糟糕了。然而,我决定使用AWS IAM机制为存储桶设置一个仅写策略,并在小程序中存储相应配置的新用户的凭据。从安全角度来看,这应该没问题。 - schneck
@schneck:实现IAM只写策略确实是一个很好的替代方案,我过于关注你如何实现目标而不是实际用例 - 你甚至可以通过使用IAM用户临时凭证进行请求进一步避免在你的小应用程序中存储永久凭证。 - Steffen Opel
一个只写、公开的存储桶的危险不就是有人可以仅使用curl来垃圾邮件轰炸你的存储桶吗?或者我漏掉了什么? - sethcall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接