更新时间:2026-04-02 GMT+08:00
分享

异步断点续传上传(Java SDK)

功能说明

异步断点续传上传是异步执行的断点续传上传,并且支持暂停、取消、继续等功能。其原理是创建一个子线程,在子线程中进行断点续传上传,上传失败产生异常时可以通过回调获取异常,上传成功时可以从task中获取Optional封装的结果。

开发过程中,您有任何问题可以在github上提交issue,或者在华为云对象存储服务论坛中发帖求助。

接口约束

  • 您必须是桶拥有者或拥有上传对象的权限,才能上传对象。建议使用IAM或桶策略进行授权,如果使用IAM则需授予obs:object:PutObject权限,如果使用桶策略则需授予PutObject权限。相关授权方式介绍可参见OBS权限控制概述,配置方式详见使用IAM自定义策略配置对象策略
  • OBS支持的region以及region与endPoint的对应关系,详细信息请参见地区与终端节点
  • 断点续传上传接口传入的文件大小至少要100K以上。
  • 使用SDK的断点续传接口时,必须开启断点续传选项后才能在进程再次进入时读取上一次上传的进度。

方法定义

obsClientAsync.uploadFileAsync(UploadFileRequestrequest, TaskCallback<CompleteMultipartUploadResult, UploadFileRequest> completeCallback)

请求参数说明

表1 uploadFile请求参数

参数

类型

是否必选

描述

request

UploadFileRequest

参数解释

上传对象请求参数,详见UploadFileRequest

completeCallback

TaskCallback

<CompleteMultipartUploadResult, UploadFileRequest>

参数解释

异步上传时,用于获取上传结果和异常的回调

表2 UploadFileRequest

参数

类型

是否必选

描述

bucketName

String

必选

参数解释

桶名。

约束限制:

  • 桶的名字需全局唯一,不能与已有的任何桶名称重复,包括其他用户创建的桶。
  • 桶命名规则如下:
    • 3~63个字符,数字或字母开头,支持小写字母、数字、“-”、“.”。
    • 禁止使用IP地址。
    • 禁止以“-”或“.”开头及结尾。
    • 禁止两个“.”相邻(如:“my..bucket”)。
    • 禁止“.”和“-”相邻(如:“my-.bucket”和“my.-bucket”)。
  • 同一用户在同一个区域多次创建同名桶不会报错,创建的桶属性以第一次请求为准。

默认取值:

objectKey

String

必选

参数解释:

对象名。对象名是对象在存储桶中的唯一标识。对象名是对象在桶中的完整路径,路径中不包含桶名。

例如,您对象的访问地址为examplebucket.obs.cn-north-4.myhuaweicloud.com/folder/test.txt 中,对象名为folder/test.txt。

取值范围:

长度大于0且不超过1024的字符串。

默认取值:

objectMetadata

ObjectMetadata

可选

参数解释:

对象元数据,详见ObjectMetadata

默认取值:

acl

AccessControlList

可选

参数解释

创桶时可指定桶的ACL,您可以使用预定义的ACL,也可以自定义ACL,有关访问控制列表(Access Control List,ACL)功能的详细信息可参见ACL功能介绍

取值范围:

默认取值:

AccessControlList.REST_CANNED_PRIVATE

sseKmsHeader

SseKmsHeader

可选

参数解释:

服务端加密头信息。详见SseKmsHeader

默认取值:

sseCHeader

SseCHeader

可选

参数解释:

服务端加密头信息。详见SseCHeader

默认取值:

enableCheckpoint

boolean

可选

参数解释:

是否开启断点续传模式。

取值范围:

true:开启断点续传模式。

false:关闭断点续传模式。

默认取值:

false

checkpointFile

String

可选

参数解释:

断点续传过程中,会生成一个进度记录文件,文件中会记录段的上传进度和段的相关信息。checkpointFile参数为该记录文件的文件路径。

约束限制:

仅在断点续传模式下有效。

默认取值:

当该值为空时,默认为待上传的本地文件的同级目录。

uploadFile

String

必选

参数解释:

待上传文件或文件夹的完整路径,如aa/bb.txt,或aa/。

默认取值:

extensionPermissionMap

Map<ExtensionObjectPermissionEnum, Set<String>>

可选

参数解释:

桶ACL的授权Map,您可以为一个或多个账号授予桶权限。Map的ExtensionObjectPermissionEnum用于指定权限,Map的Set<String>用于说明该权限授予的账号ID列表,即domain_id列表。

取值范围:

默认取值:

progressListener

ProgressListener

可选

参数解释:

设置数据传输监听器,用于获取上传进度。详见ProgressListener

partSize

long

可选

参数解释:

分段大小。

取值范围:

100KB~5GB,单位:字节。

默认取值:

9MB

taskNum

int

可选

参数解释:

分段上传时的最大并发数。

取值范围:

1~10000,单位:个。

默认取值:

1,即不设置则默认单任务上传。

encodeHeaders

boolean

可选

参数解释:

是否开启OBS对请求头域的自动编码。

由于HTTP编码规范限制,无法发送非ASCII码字符,SDK会在发送请求时对您头域中的中文汉字进行url编码,发送编码后数据。如您设置的值content-disposition为attachment; filename="中文.txt",则对象元数据中存储的信息为attachment; filename="%E4%B8%AD%E6%96%87.txt"。使用浏览器访问时浏览器将会自动解码。

取值范围:

true:启用SDK编码。

false:不启用SDK编码。

默认取值:

true

needCalculateCRC64

boolean

可选

参数解释:

是否自动计算待上传数据的crc64值并提交服务端校验。

约束限制:

不支持POSIX、SFS对象。

取值范围:

true:表示由SDK计算crc64并提交服务端校验。

false:不检验crc64。

默认取值:

false

表3 TaskCallback<result, request>泛型接口

方法名称

返回值

参数类型

描述

onSuccess

void

result(泛型)

成功时执行的回调,参数是请求成功的结果

onException

void

ObsException, request(泛型)

异常时执行的回调,参数是捕获的异常和出现异常的请求

返回结果说明

表4 UploadFileTask

方法名称

返回值

参数类型

描述

isTaskFinished

boolean

判断任务是否执行完毕

waitUntilFinished

void

阻塞当前线程,等待异步上传任务结束

cancel

boolean

取消异步上传任务,返回true时执行取消成功,false说明执行失败

getResult

java.util.Optional

<CompleteMultipartUploadResult>

阻塞当前线程,等待异步上传任务结束,然后返回任务执行结果, 上传失败时Optional.isPresent()为false

表5 CompleteMultipartUploadResult

参数名称

参数类型

描述

statusCode

int

参数解释:

HTTP状态码。

取值范围:

状态码是一组从2xx(成功)到4xx或5xx(错误)的数字代码,状态码表示了请求响应的状态。

完整的状态码列表请参见状态码

默认取值:

responseHeaders

Map<String, Object>

参数解释:

响应消息头列表,由多个元组构成。元组中String代表响应消息头的名称,Object代表响应消息头的值。

默认取值:

bucketName

String

参数解释

桶名。

约束限制:

  • 桶的名字需全局唯一,不能与已有的任何桶名称重复,包括其他用户创建的桶。
  • 桶命名规则如下:
    • 3~63个字符,数字或字母开头,支持小写字母、数字、“-”、“.”。
    • 禁止使用IP地址。
    • 禁止以“-”或“.”开头及结尾。
    • 禁止两个“.”相邻(如:“my..bucket”)。
    • 禁止“.”和“-”相邻(如:“my-.bucket”和“my.-bucket”)。
  • 同一用户在同一个区域多次创建同名桶不会报错,创建的桶属性以第一次请求为准。

默认取值:

objectKey

String

参数解释:

对象名。对象名是对象在存储桶中的唯一标识。对象名是对象在桶中的完整路径,路径中不包含桶名。

例如,您对象的访问地址为examplebucket.obs.cn-north-4.myhuaweicloud.com/folder/test.txt 中,对象名为folder/test.txt。

取值范围:

长度大于0且不超过1024的字符串。

默认取值:

etag

String

参数解释:

对象的etag值,即Base64编码的128位MD5摘要。etag是对象内容的唯一标识,可以通过该值识别对象内容是否有变化。比如上传对象时etag为A,下载对象时etag为B,则说明对象内容发生了变化。etag只反映变化的内容,而不是其元数据。上传的对象或拷贝操作创建的对象,都有唯一的etag。

约束限制:

当对象是服务端加密的对象时,etag值不是对象的MD5值。

取值范围:

长度为32的字符串。

默认取值:

versionId

String

参数解释:

对象的版本号。如果桶的多版本状态为开启,则会返回对象的版本号。

取值范围:

长度为32的字符串。

默认取值:

代码示例

本示例展示了如何异步断点续传,将本地文件localfile上传到examplebucket桶下的objectKey对象中。同时演示了暂停上传和继续上传的功能。
import com.obs.services.ObsClientAsync;
import com.obs.services.exception.ObsException;
import com.obs.services.internal.task.UploadFileTask;
import com.obs.services.internal.utils.CallCancelHandler;
import com.obs.services.model.CompleteMultipartUploadResult;
import com.obs.services.model.TaskCallback;
import com.obs.services.model.UploadFileRequest;

import java.io.IOException;
import java.util.Map;
import java.util.Optional;

public class TestUploadFileCancel
{
    public static void main(String[] args) {
        // 您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入。如果使用硬编码可能会存在泄露风险。
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        String ak = System.getenv("ACCESS_KEY_ID");
        String sk = System.getenv("SECRET_ACCESS_KEY_ID");
        // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。
        // 您可以通过环境变量获取访问密钥AK/SK/SecurityToken,也可以使用其他外部引入方式传入。
        String securityToken = System.getenv("SECURITY_TOKEN");
        // endpoint填写桶所在的endpoint, 此处以华北-北京四为例,其他地区请按实际情况填写。
        String endPoint = "https://obs.cn-north-4.myhuaweicloud.com";
        // 您可以通过环境变量获取endPoint,也可以使用其他外部引入方式传入。
        //String endPoint = System.getenv("ENDPOINT");

        // 创建ObsClient实例
        try (ObsClientAsync obsClientAsync = new ObsClientAsync(ak, sk, securityToken, endPoint)) {
            String bucketName = "examplebucket";
            String objectKey = "objectKey";
            String localFile = "localfile";
            UploadFileRequest uploadFileRequest1 = new UploadFileRequest(bucketName, objectKey, localFile);
            uploadFileRequest1.setProgressListener(status -> {
                // 获取上传平均速率
                System.out.println("AverageSpeed:" + status.getAverageSpeed());
                // 获取上传进度百分比
                System.out.println("TransferPercentage:" + status.getTransferPercentage());
            });
            CallCancelHandler callCancelHandler = new CallCancelHandler();
            uploadFileRequest1.setCancelHandler(callCancelHandler);
            // 每上传5MB数据反馈上传进度
            uploadFileRequest1.setProgressInterval(5 * 1024 * 1024L);
            // 开启进度记录,用于断点续传
            uploadFileRequest1.setEnableCheckpoint(true);
            // 5个并发线程上传
            uploadFileRequest1.setTaskNum(5);
            // 分段大小5MB
            uploadFileRequest1.setPartSize(5 * 1024 * 1024L);
            //暂停上传后不取消上传任务,不影响已经成功上传的分段
            uploadFileRequest1.setNeedAbortUploadFileAfterCancel(false);
            // 成功回调和异常回调
            TaskCallback<CompleteMultipartUploadResult,UploadFileRequest> completeCallback = new TaskCallback<CompleteMultipartUploadResult, UploadFileRequest>()
            {
                @Override
                public void onSuccess(CompleteMultipartUploadResult result)
                {}
                @Override
                public void onException(ObsException e, UploadFileRequest singleRequest)
                {
                    System.out.println("uploadFileAsync failed");
                    if(singleRequest.getCancelHandler()!=null) {
                        System.out.println("UploadFileRequest isCanceled ? " + singleRequest.getCancelHandler().isCancelled());
                    }
                    // 请求失败,打印http状态码
                    System.out.println("HTTP Code:" + e.getResponseCode());
                    // 请求失败,打印服务端错误码
                    System.out.println("Error Code:" + e.getErrorCode());
                    // 请求失败,打印详细错误信息
                    System.out.println("Error Message:" + e.getErrorMessage());
                    // 请求失败,打印请求id
                    System.out.println("Request ID:" + e.getErrorRequestId());
                    System.out.println("Host ID:" + e.getErrorHostId());
                    // 遍历Map的entry,打印所有报错相关头域
                    Map<String, String> headers = e.getResponseHeaders();
                    if(headers != null){
                        for (Map.Entry<String, String> header : headers.entrySet()) {
                            if(header.getKey().contains("error")){
                                System.out.println("errorHeaderKey:"+header.getKey()+", errorHeaderValue:"+header.getValue());
                            }
                        }
                    }
                    e.printStackTrace();
                }
            };
            // 开始异步断点续传上传
            UploadFileTask uploadFileTask = obsClientAsync.uploadFileAsync(uploadFileRequest1, completeCallback);
            Thread.sleep(1000);
            // 异步上传一段时间后,暂停上传, 已经上传完的分段无法暂停
            uploadFileTask.cancel();
            // 等待异步上传任务结束
            uploadFileTask.waitUntilFinished();
            // 继续上次的进度进行断点续传上传
            uploadFileTask = obsClientAsync.uploadFileAsync(uploadFileRequest1, completeCallback);
            Optional<CompleteMultipartUploadResult> result = uploadFileTask.getResult();
            if (result.isPresent()) {
                System.out.println("uploadFileAsync Successfully!");
                System.out.println("HTTP StatusCode:"+result.get().getStatusCode());
                System.out.println("ObjectUrl:"+result.get().getObjectUrl());
                System.out.println("Etag:"+result.get().getEtag());
            }
        }
        catch (IOException | InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

本示例展示了如何异步断点续传,将本地文件localfile上传到examplebucket桶下的objectKey对象中。同时演示了暂停并取消断点续传上传的功能。

import com.obs.services.ObsClientAsync;
import com.obs.services.exception.ObsException;
import com.obs.services.internal.task.UploadFileTask;
import com.obs.services.internal.utils.CallCancelHandler;
import com.obs.services.model.CompleteMultipartUploadResult;
import com.obs.services.model.TaskCallback;
import com.obs.services.model.UploadFileRequest;

import java.io.IOException;
import java.util.Map;

public class TestUploadFileCancelAndAbort
{
    public static void main(String[] args) {
        // 您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入。如果使用硬编码可能会存在泄露风险。
        // 您可以登录访问管理控制台获取访问密钥AK/SK
        String ak = System.getenv("ACCESS_KEY_ID");
        String sk = System.getenv("SECRET_ACCESS_KEY_ID");
        // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。
        // 您可以通过环境变量获取访问密钥AK/SK/SecurityToken,也可以使用其他外部引入方式传入。
        String securityToken = System.getenv("SECURITY_TOKEN");

        // endpoint填写桶所在的endpoint, 此处以华北-北京四为例,其他地区请按实际情况填写。
        String endPoint = "https://obs.cn-north-4.myhuaweicloud.com";
        // 您可以通过环境变量获取endPoint,也可以使用其他外部引入方式传入。
        //String endPoint = System.getenv("ENDPOINT");

        // 创建ObsClient实例
        try (ObsClientAsync obsClientAsync = new ObsClientAsync(ak, sk, securityToken, endPoint)) {
            String bucketName = "examplebucket";
            String objectKey = "objectKey";
            String localFile = "localfile";
            UploadFileRequest uploadFileRequest1 = new UploadFileRequest(bucketName, objectKey, localFile);
            uploadFileRequest1.setProgressListener(status -> {
                // 获取上传平均速率
                System.out.println("AverageSpeed:" + status.getAverageSpeed());
                // 获取上传进度百分比
                System.out.println("TransferPercentage:" + status.getTransferPercentage());
            });
            CallCancelHandler callCancelHandler = new CallCancelHandler();
            uploadFileRequest1.setCancelHandler(callCancelHandler);
            // 每上传5MB数据反馈上传进度
            uploadFileRequest1.setProgressInterval(5 * 1024 * 1024L);
            // 5个并发线程上传
            uploadFileRequest1.setTaskNum(5);
            // 分段大小5mb
            uploadFileRequest1.setPartSize(5 * 1024 * 1024L);
            //暂停上传后同时取消分段上传任务,删除已经成功上传的分段
            uploadFileRequest1.setNeedAbortUploadFileAfterCancel(true);
            // 成功回调和异常回调
            TaskCallback<CompleteMultipartUploadResult,UploadFileRequest> completeCallback =
                    new TaskCallback<CompleteMultipartUploadResult, UploadFileRequest>()
            {
                @Override
                public void onSuccess(CompleteMultipartUploadResult result)
                {}
                @Override
                public void onException(ObsException e, UploadFileRequest singleRequest)
                {
                    System.out.println("uploadFileAsync failed");
                    if(singleRequest.getCancelHandler()!=null) {
                        System.out.println("UploadFileRequest isCanceled ? " + singleRequest.getCancelHandler().isCancelled());
                    }
                    // 请求失败,打印http状态码
                    System.out.println("HTTP Code:" + e.getResponseCode());
                    // 请求失败,打印服务端错误码
                    System.out.println("Error Code:" + e.getErrorCode());
                    // 请求失败,打印详细错误信息
                    System.out.println("Error Message:" + e.getErrorMessage());
                    // 请求失败,打印请求id
                    System.out.println("Request ID:" + e.getErrorRequestId());
                    System.out.println("Host ID:" + e.getErrorHostId());
                    // 遍历Map的entry,打印所有报错相关头域
                    Map<String, String> headers = e.getResponseHeaders();
                    if(headers != null){
                        for (Map.Entry<String, String> header : headers.entrySet()) {
                            if(header.getKey().contains("error")){
                                System.out.println("errorHeaderKey:"+header.getKey()+", errorHeaderValue:"+header.getValue());
                            }
                        }
                    }
                    e.printStackTrace();
                }
            };
            // 开始异步断点续传上传
            UploadFileTask uploadFileTask = obsClientAsync.uploadFileAsync(uploadFileRequest1, completeCallback);
            Thread.sleep(1000);
            // 异步上传一段时间后,暂停上传, 自动取消分段上传任务,删除已经成功上传的分段, 如果上传已经完成则不能取消
            uploadFileTask.cancel();
            // 等待异步上传任务结束
            uploadFileTask.waitUntilFinished();
        }
        catch (IOException | InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

相关链接

  • 上传对象过程中返回的错误码含义、问题原因及处理措施可参考OBS错误码
  • 上传对象常见问题请参见上传对象失败

相关文档