标准接口
简单上传方式只能上传小于5GB的对象,如果需要上传超过5GB的对象,您必须使用分片上传模式。此外,您也可以在如下的应用场景内(但不仅限于此),使用分片上传模式:
- 上传超过100MB大小的文件。
- 上传文件之前,无法确定上传文件的大小。
- 需要较高的吞吐量(采用并发上传分片)。
- 网络条件较差,和服务器之间的链接经常断开。
使用分片上传时,需要分为如下3个步骤:
- 初始化一个分片上传任务,使用 InitiateMultipartUpload 接口
- 使用 UploadPart 接口逐个或并行上传分片,或者使用 CopyPart 接口复制分片
- 完成分片上传,使用 CompleteMultipartUpload 接口
使用Multipart Upload模式传输数据前,必须先初始化一个Multipart Upload事件,Multipart Upload事件可用于上传或复制对象。该操作会返回一个服务器创建的全局唯一的Upload ID,用于标识本次Multipart Upload事件。用户可以根据这个ID来发起相关的操作,如中止Multipart Upload、查询Multipart Upload等。
String bucketName = "<your-bucket-name>";
String objectKey = "<your-object-key>";
//指定桶与对象名,也可以在request中同时设置metadata,acl等对象的属性
InitiateMultipartUploadRequest initUploadRequest = new
InitiateMultipartUploadRequest(bucketName, objectKey);
InitiateMultipartUploadResult initResult =
s3.initiateMultipartUpload(initUploadRequest);
//用于区分分片上传的唯一标识,后续的操作中会使用该id
String uploadId = initResult.getUploadId();
初始化一个Multipart Upload之后,可以根据指定的对象名和Upload ID来分片(Part)上传数据。每一个上传的Part都有一个标识它的号码——分片号(part number,范围是1~10000)。对于同一个Upload ID,该分片号不但唯一标识这一块数据,也标识了这块数据在整个文件内的相对位置,您需要在UploadPartRequest中指定Upload ID与分片号。如果用同一个分片号码,上传了新的数据,那么已有的这个分片的数据将被覆盖。除了最后一块part以外,其他的part最小为5MB;最后一块part没有大小限制。分片不需要按顺序上传,甚至可以在不同进程、不同机器上上传,在完成分片上传后服务端会按照分片号排序组成大文件。上传分片部分代码如下:
//分片大小为5M
long partSize = 5 * 1024 * 1024;
long filePosition = 0;
for (int partNum = 1; filePosition < contentLength; partNum++) {
partSize = Math.min(partSize, (contentLength - filePosition));
// 创建分片复制请求
UploadPartRequest uploadRequest = new UploadPartRequest()
.withBucketName(bucketName)
.withKey(objectKey)
//uploadId为initiateMultipartUpload中返回值
.withUploadId(uploadId)
//设置分片号
.withPartNumber(partNum)
//设置文件数据偏移量
.withFileOffset(filePosition)
//设置文件
.withFile(file)
//设置分片大小,最小为5MB
.withPartSize(partSize);
// 上传分片并把ETag放入list中.
UploadPartResult uploadResult = s3.uploadPart(uploadRequest);
partETags.add(uploadResult.getPartETag());
filePosition += partSize;
}
您可以初始化一个Multipart Upload后,通过copyPart接口分片复制某一个对象,您需要在CopyPartRequest中设置复制的源桶名对象名,目标桶名对象名,Upload ID来分片(Part)复制对象。与上传分片类似的,每一个复制的分片都有一个分片号(part number),您也需要在CopyPartRequest中设置分片号标识复制的分片。复制分片部分代码如下:
//分片大小为5M
long partSize = 5 * 1024 * 1024;
long bytePosition = 0;
int partNum = 1;
List<CopyPartResult> copyResponses = new ArrayList<CopyPartResult>();
while (bytePosition < objectSize) {
long lastByte = Math.min(bytePosition + partSize - 1, objectSize - 1);
// 创建分片复制请求
CopyPartRequest copyRequest = new CopyPartRequest()
//设置源桶和对象,目标桶和对象
.withSourceBucketName(sourceBucketName)
.withSourceKey(sourceObjectKey)
.withDestinationBucketName(destBucketName)
.withDestinationKey(destObjectKey)
//uploadId为initiateMultipartUpload中返回值
.withUploadId(initResult.getUploadId())
//设置分片复制范围
.withFirstByte(bytePosition)
.withLastByte(lastByte)
//设置分片号
.withPartNumber(partNum++);
CopyPartResult copyPartResult = s3.copyPart(copyRequest);
partETags.add(copyPartResult.getPartETag());
bytePosition += partSize;
}
所有分片上传或复制完成后,需要调用 Complete Multipart Upload 来完成整个文件的 Multipart Upload。在执行该操作时,需要提供所有有效的分片列表(包括分片号和分片 ETAG );服务端收到提交的分片列表后,会逐一验证每个分片的有效性。当所有的数据Part验证通过后,服务端将把这些分片组合成一个完整的 Object。完成分片上传示例代码如下:
CompleteMultipartUploadRequest completeMultipartUploadRequest =
new CompleteMultipartUploadRequest(bucketName, objectKey, uploadId, partETags);
//成功完成分片上传后,服务端会返回该对象的信息
CompleteMultipartUploadResult result =
s3.completeMultipartUpload(completeMultipartUploadRequest);
若是完成分片复制,CompleteMultipartUploadRequest 中 bucketName 和 objectKey 设置为目标的桶和对象名,示例代码如下:
//完成分片复制
CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(
//CompleteMultipartUploadRequest设置目标桶和对象名
destBucketName,
destObjectKey,
initResult.getUploadId(),
partETags);
s3.completeMultipartUpload(completeRequest);
您可以通过 listParts 接口根据 Upload ID 获取所有已经上传成功的分片。默认每次请求返回的分片数为1000。
ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName,objectKey,UploadId);
PartListing partListing = s3.listParts(listPartsRequest);
for (PartSummary part : partListing.getParts()) {
// 分片号,上传时候指定
part.getPartNumber();
// Part大小
part.getSize();
// Part的ETag
part.getETag();
// Part的最后修改上传
part.getLastModified();
}
如果分片数大于1000,返回的 PartListing 中 isTruncated 为 true ,并可以根据 NextPartNumberMarker 为下一次请求的 list 的起始位置。
PartListing partListing;
ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectKey, uploadId);
do {
partListing = s3.listParts(listPartsRequest);
for (PartSummary part : partListing.getParts()) {
// 分片号,上传时候指定
part.getPartNumber();
// Part大小
part.getSize();
// Part的ETag
part.getETag();
// Part的最后修改上传
part.getLastModified();
}
listPartsRequest.setPartNumberMarker(partListing.getNextPartNumberMarker());
} while (partListing.isTruncated());
该接口可以根据 Upload ID 中止对应的 Multipart Upload。当一个 Multipart Upload 被中止后,就不能再使用这个 Upload ID 做任何操作,已经上传的 Part 数据也会被删除。
//uploadId为initiateMultipartUpload中返回值
AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucketName, objectKey, uploadId);
s3.abortMultipartUpload(abortMultipartUploadRequest);
您可以通过 listMultipartUploadsRequest 接口列举分片上传事件,包括已经初始化的尚未Complete或者Abort的分片上传事件。默认情况下,每次list操作最多返回1000个分片上传事件。以下代码展示如何简单列举分片上传事件:
String bucketName = "<your-bucket-name>";
ListMultipartUploadsRequest listMultipartUploadsRequest = new
ListMultipartUploadsRequest(bucketName);
MultipartUploadListing multipartUploadListing =
s3.listMultipartUploads(listMultipartUploadsRequest);
for (MultipartUpload multipartUpload : multipartUploadListing.getMultipartUploads()) {
multipartUpload.getUploadId();
multipartUpload.getInitiator();
multipartUpload.getInitiated();
multipartUpload.getKey();
}
如果list大于1000,则返回的结果中 isTruncated 为true,并返回 NextKeyMarker NextUploadIdMarker 作为下次读取的起点。如果没有一次性获取所有的分片上传事件,可以采用分页列举的方式。列举所有分片上传事件示例代码如下:
String bucketName = "<your-bucket-name>";
MultipartUploadListing multipartUploadListing;
ListMultipartUploadsRequest listMultipartUploadsRequest = new
ListMultipartUploadsRequest(bucketName);
do {
multipartUploadListing = s3.listMultipartUploads(listMultipartUploadsRequest);
for (MultipartUpload multipartUpload : multipartUploadListing.getMultipartUploads()) {
multipartUpload.getUploadId();
multipartUpload.getInitiator();
multipartUpload.getInitiated();
multipartUpload.getKey();
}
listMultipartUploadsRequest.setKeyMarker(multipartUploadListing.getNextKeyMarker());
listMultipartUploadsRequest.setUploadIdMarker(multipartUploadListing.getNextUploadIdMarker());
} while (multipartUploadListing.isTruncated());