本文介绍如何使用分片上传。

分片上传(Multipart Upload)分为以下三个步骤:

  1. 初始化一个分片上传事件。

    调用oss.initMultipartUpload方法返回OSS创建的全局唯一的uploadId。

  2. 上传分片。

    调用oss.uploadPart方法上传分片数据。

    说明
    • 对于同一个uploadId,分片号(partNumber)标识了该分片在整个文件内的相对位置。如果使用同一个分片号上传了新的数据,那么OSS上这个分片已有的数据将会被覆盖。
    • OSS将收到的分片数据的MD5值放在ETag头内返回给用户。
    • OSS计算上传数据的MD5值,并与SDK计算的MD5值比较,如果不一致则返回InvalidDigest错误码。
  3. 完成分片上传。

    所有分片上传完成后,调用oss.CompleteMultipartUpload方法将所有分片合并成完整的文件。

以下通过一个完整的示例对分片上传的流程进行逐步解析:

    //初始化分片上传。
    //InitiateMultipartUploadRequest用于指定上传文件的名称以及上传文件所属的存储空间的名称。
    InitiateMultipartUploadRequest init = new InitiateMultipartUploadRequest("<bucketName>", "<objectKey>");
    InitiateMultipartUploadResult initResult = oss.initMultipartUpload(init);
    //initResult返回的结果中包含UploadId,UploadId用于唯一标识本次Multipart Upload事件。
    String uploadId = initResult.getUploadId();

    int partCount = 100*1024;
    //分片上传
    for (int i = 1;i<5;i++){
    byte[] data = new byte[partCount];

    RandomAccessFile raf = new RandomAccessFile("path", "r");
    long skip = (i-1) * partCount;
    raf.seek(skip);
    raf.readFully(data, 0, partCount);

    UploadPartRequest uploadPart = new UploadPartRequest();
    uploadPart.setBucketName(mBucketName);
    uploadPart.setObjectKey(objectKey);
    uploadPart.setUploadId(uploadId);
    uploadPart.setPartNumber(i); //上传的Part对应的part number从1开始标识。
    uploadPart.setPartContent(data);
    try {
        oss.uploadPart(uploadPart);
    } catch (ServiceException serviceException) {
        OSSLog.logError(serviceException.getErrorCode());
    }
}

    //完成分片上传。
    CompleteMultipartUploadRequest complete = new CompleteMultipartUploadRequest("<bucketName>", "<objectName>", "<uploadId>", "<partETagList>";

    //上传回调。完成分片上传请求时可以设置CALLBACK_SERVER参数,请求完成后会向指定的Server Address发送回调请求。可通过返回结果的completeResult.getServerCallbackReturnBody()查看servercallback结果。
    complete.setCallbackParam(new HashMap<String, String>() {
        {
            put("callbackUrl", CALLBACK_SERVER); //修改为您的服务器地址。
            put("callbackBody", "test");
        }
    });
    CompleteMultipartUploadResult completeResult = oss.completeMultipartUpload(complete);
    OSSLog.logError("-------------- serverCallback: " + completeResult.getServerCallbackReturnBody());

上述代码调用 uploadPart 来上传每一个分片。

注意
  • 每一个分片上传请求需指定 UploadId 和 PartNum 。
  • uploadPart 要求除最后一个 Part 外,其他的 Part 大小都要大于100KB。但是 Upload Part 接口并不会立即校验上传。只有完成分片上传时才会校验 Part 的大小。
  • Part 号码的范围是1~10000。如果超出这个范围,OSS 将返回 InvalidArgument 的错误码。
  • 每次上传 Part 时都要把流定位到此次上传片开头所对应的位置。
  • 每次上传 Part 之后,OSS 的返回结果会包含一个分片的 ETag值,ETag 值为 Part 数据的 MD5 值,您需要将 ETag 值和块编号组合成 PartEtag 并保存,用于后续完成分片上传。

列举已上传分片

调用 oss.listParts 方法获取某个上传事件所有已上传的分片。

以下代码用于列举已上传分片:

    //列举分片。
    ListPartsRequest listParts = new ListPartsRequest("<bucketName>", "<objectName>", "<uploadId>");
    ListPartsResult result = oss.listParts(listParts);

    List<PartETag> partETagList = new ArrayList<PartETag>();
    for (PartSummary part : result.getParts()) {
        partETagList.add(new PartETag(part.getPartNumber(), part.getETag()));
    }
注意 默认情况下,如果存储空间中的分片上传事件的数量大于 1000,则只会返回 1000 个 Multipart Upload 信息,且返回结果中 IsTruncated 为 false,并返回 NextPartNumberMarker 作为下此读取的起点。

取消分片上传

调用 oss.abortMultipartUpload 方法取消对应 UploadId 的分片上传请求。当一个分片上传事件被取消后,无法再使用这个 uploadId 做任何操作,已经上传的分片数据会被删除。

以下代码用于取消分片上传:

    //取消分片上传。
    AbortMultipartUploadRequest abort = new AbortMultipartUploadRequest("<bucketName>", "<objectName>", "<uploadId>");
    AbortMultipartUploadResult abortResult = oss.abortMultipartUpload(abort);

本地文件分片上传

通过获取文件路径进行本地文件分片上传有以下两种方法。

  • 方法一
    ObjectMetadata meta = new ObjectMetadata();
            //设置文件元信息等。
            meta.setHeader("x-oss-object-acl", "public-read-write");
            MultipartUploadRequest rq = new MultipartUploadRequest("<bucketName>", "<objkey>",
                    "<filepath>", meta);
            //设置PartSize。PartSize默认值为256KB,最小值为100KB。
            rq.setPartSize(1024 * 1024);
            rq.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() {
                @Override
                public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) {
                    OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false);
                }
            });
    
            CompleteMultipartUploadResult result = oss.multipartUpload(rq);
  • 方式二
    MultipartUploadRequest request = new MultipartUploadRequest("<bucketName>", "<objkey>",
                    "<filepath>");
    
        request.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() {
    
            @Override
            public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) {
                OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false);
            }
        });
    
        OSSAsyncTask task = oss.asyncMultipartUpload(request, new OSSCompletedCallback<MultipartUploadRequest, CompleteMultipartUploadResult>() {
        @Override
        public void onSuccess(MultipartUploadRequest request, CompleteMultipartUploadResult result) {
            OSSLog.logInfo(result.getServerCallbackReturnBody());
        }
    
        @Override
        public void onFailure(MultipartUploadRequest request, ClientException clientException, ServiceException serviceException) {
            OSSLog.logError(serviceException.getRawMessage());
    
        }
    });
    
       // Thread.sleep(100);
       //取消分片上传。
       // task.cancel();   
    
        task.waitUntilFinished();