You need to enable JavaScript to run this app.
导航

断点续传下载(Java SDK)

最近更新时间2024.02.04 18:31:01

首次发布时间2022.11.18 16:35:45

下载大对象时,可能出现网络波动等情况导致下载失败。TOS Java SDK 提供了重试机制保障下载对象的稳定性,但仍可能出现多次重试后仍无法完成下载的情况。针对上述问题,Java SDK 提供了断点续传下载的功能,在大对象下载过程中如果出现下载失败,可再次调用接口,从上次下载的进度处继续下载。使用 Java SDK 的断点续传接口时,您可以自定义设置分片大小、下载的并发线程数、下载的客户端限速、事件回调函数等,也支持在断点续传下载任务执行过程中,取消该任务。

注意事项

  • 下载对象前,您必须具有 tos:GetObject 权限,具体操作,请参见权限配置指南
  • 对于开启多版本的桶,下载指定版本对象时,您必须具有 tos:GetObjectVersion 权限,具体操作,请参见权限配置指南
  • 如果应用程序会在同一时刻大量下载同一个对象,您的访问速度会受到 TOS 带宽及地域的限制。建议您使用 CDN 产品,提升性能的同时也能降低您的成本。通过 CDN 访问 TOS 的详细信息,请参见使用 CDN 加速访问 TOS 资源

基本示例

以下代码展示 SDK 断点续传下载接口 downloadFile 的基本使用方式。

import com.volcengine.tos.TOSV2;
import com.volcengine.tos.TOSV2ClientBuilder;
import com.volcengine.tos.TosClientException;
import com.volcengine.tos.TosServerException;
import com.volcengine.tos.model.object.DownloadFileInput;
import com.volcengine.tos.model.object.DownloadFileOutput;

public class DownloadFileExample {
    public static void main(String[] args) {
        String endpoint = "your endpoint";
        String region = "your region";
        String accessKey = System.getenv("TOS_ACCESS_KEY");
        String secretKey = System.getenv("TOS_SECRET_KEY");

        String bucketName = "bucket-example";
        // 对象名,如果对象名以 "/"(linux 或 macOS 系统) 或 "\"(Windows 系统)结尾,将在本地生成对应空文件夹
        String objectKey = "example_dir/example_object.txt";

        // downloadFilePath 设置待下载的文件路径,建议使用绝对路径,确保路径下不存在文件,否则会将其覆盖。
        String downloadFilePath = "example_dir/example_file.txt";
        // taskNum 设置并发上传的并发数,范围为 1-1000
        int taskNum = 5;
        // partSize 设置文件分片大小,范围为 5MB - 5GB,默认为 20MB
        long partSize = 10 * 1024 * 1024;
        // enableCheckpoint 设置是否开启断点续传功能,开启则会在本地记录上传进度
        boolean enableCheckpoint = true;
        // checkpointFilePath 设置断点续传记录文件存放位置,若不设置则默认在 downloadFilePath 路径下生成
        // 其格式为 {downloadFilePath}.{bucket+objectKey+versionID 的 Base64Md5 值}.download
        String checkpointFilePath = "the checkpoint file path";

        TOSV2 tos = new TOSV2ClientBuilder().build(region, endpoint, accessKey, secretKey);

        try{
            DownloadFileInput input = new DownloadFileInput().setBucket(bucketName).setKey(objectKey)
                    .setFilePath(downloadFilePath).setEnableCheckpoint(enableCheckpoint)
                    .setCheckpointFile(checkpointFilePath).setPartSize(partSize).setTaskNum(taskNum);
            DownloadFileOutput output = tos.downloadFile(input);
            System.out.println("downloadFile succeed, object's etag is " + output.getEtag());
            System.out.println("downloadFile succeed, object's crc64 is " + output.getHashCrc64ecma());
        } catch (TosClientException e) {
            // 操作失败,捕获客户端异常,一般情况是请求参数错误,此时请求并未发送
            System.out.println("downloadFile failed");
            System.out.println("Message: " + e.getMessage());
            if (e.getCause() != null) {
                e.getCause().printStackTrace();
            }
        } catch (TosServerException e) {
            // 操作失败,捕获服务端异常,可以获取到从服务端返回的详细错误信息
            System.out.println("downloadFile failed");
            System.out.println("StatusCode: " + e.getStatusCode());
            System.out.println("Code: " + e.getCode());
            System.out.println("Message: " + e.getMessage());
            System.out.println("RequestID: " + e.getRequestID());
        } catch (Throwable t) {
            // 作为兜底捕获其他异常,一般不会执行到这里
            System.out.println("downloadFile failed");
            System.out.println("unexpected exception, message: " + t.getMessage());
        }
    }
}

事件回调

以下代码展示如何在断点续传下载过程中自定义监听回调函数。

import com.volcengine.tos.TOSV2;
import com.volcengine.tos.TOSV2ClientBuilder;
import com.volcengine.tos.TosClientException;
import com.volcengine.tos.TosServerException;
import com.volcengine.tos.comm.event.DownloadEventType;
import com.volcengine.tos.model.object.*;

public class DownloadFileWithEventListenerExample {
    public static void main(String[] args) {
        String endpoint = "your endpoint";
        String region = "your region";
        String accessKey = System.getenv("TOS_ACCESS_KEY");
        String secretKey = System.getenv("TOS_SECRET_KEY");

        String bucketName = "bucket-example";
        // 对象名,如果对象名以 "/"(linux 或 macOS 系统) 或 "\"(Windows 系统)结尾,将在本地生成对应空文件夹
        String objectKey = "example_dir/example_object.txt";

        // downloadFilePath 设置待下载的文件路径,建议使用绝对路径,确保路径下不存在文件,否则会将其覆盖。
        String downloadFilePath = "example_dir/example_file.txt";
        // taskNum 设置并发上传的并发数,范围为 1-1000
        int taskNum = 5;
        // partSize 设置文件分片大小,范围为 5MiB - 5GiB,默认为 20MiB
        long partSize = 10 * 1024 * 1024;
        // enableCheckpoint 设置是否开启断点续传功能,开启则会在本地记录上传进度
        boolean enableCheckpoint = true;
        // checkpointFilePath 设置断点续传记录文件存放位置,若不设置则默认在 downloadFilePath 路径下生成
        // 其格式为 {downloadFilePath}.{bucket+objectKey+versionID 的 Base64Md5 值}.download
        String checkpointFilePath = "the checkpoint file path";

        TOSV2 tos = new TOSV2ClientBuilder().build(region, endpoint, accessKey, secretKey);

        try{
            HeadObjectV2Input head = new HeadObjectV2Input().setBucket(bucketName).setKey(objectKey);
            DownloadEventListener listener = new DownloadEventListener() {
                @Override
                public void eventChange(DownloadEvent downloadEvent) {
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventCreateTempFileSucceed) {
                        System.out.println("event change, createTempFile succeed");
                    }
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventCreateTempFileFailed) {
                        System.out.println("event change, createTempFile failed");
                    }
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventDownloadPartSucceed) {
                        System.out.println("event change, downloadPart succeed");
                    }
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventDownloadPartFailed) {
                        System.out.println("event change, downloadPart failed");
                    }
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventDownloadPartAborted) {
                        System.out.println("event change, downloadPart aborted");
                    }
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventRenameTempFileSucceed) {
                        System.out.println("event change, renameTempFile succeed");
                    }
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventRenameTempFileFailed) {
                        System.out.println("event change, renameTempFile failed");
                    }
                }
            };
            DownloadFileInput input = new DownloadFileInput().setHeadObjectV2Input(head)
                    .setFilePath(downloadFilePath).setEnableCheckpoint(enableCheckpoint)
                    .setCheckpointFile(checkpointFilePath).setPartSize(partSize).setTaskNum(taskNum)
                    .setDownloadEventListener(listener);
            DownloadFileOutput output = tos.downloadFile(input);
            System.out.println("downloadFile succeed, object's etag is " + output.getEtag());
            System.out.println("downloadFile succeed, object's crc64 is " + output.getHashCrc64ecma());
        } catch (TosClientException e) {
            // 操作失败,捕获客户端异常,一般情况是请求参数错误,此时请求并未发送
            System.out.println("downloadFile failed");
            System.out.println("Message: " + e.getMessage());
            if (e.getCause() != null) {
                e.getCause().printStackTrace();
            }
        } catch (TosServerException e) {
            // 操作失败,捕获服务端异常,可以获取到从服务端返回的详细错误信息
            System.out.println("downloadFile failed");
            System.out.println("StatusCode: " + e.getStatusCode());
            System.out.println("Code: " + e.getCode());
            System.out.println("Message: " + e.getMessage());
            System.out.println("RequestID: " + e.getRequestID());
        } catch (Throwable t) {
            // 作为兜底捕获其他异常,一般不会执行到这里
            System.out.println("downloadFile failed");
            System.out.println("unexpected exception, message: " + t.getMessage());
        }
    }
}

取消机制

断点续传过程中,SDK 支持取消下载任务。以下代码展示如何取消正在执行的断点续传下载任务。

import com.volcengine.tos.TOSV2;
import com.volcengine.tos.TOSV2ClientBuilder;
import com.volcengine.tos.TosClientException;
import com.volcengine.tos.TosServerException;
import com.volcengine.tos.comm.event.DownloadEventType;
import com.volcengine.tos.model.object.*;

public class DownloadFileWithCancelExample {
    public static void main(String[] args) {
        String endpoint = "your endpoint";
        String region = "your region";
        String accessKey = System.getenv("TOS_ACCESS_KEY");
        String secretKey = System.getenv("TOS_SECRET_KEY");

        String bucketName = "bucket-example";
        // 对象名,如果对象名以 "/"(linux 或 macOS 系统) 或 "\"(Windows 系统)结尾,将在本地生成对应空文件夹
        String objectKey = "example_dir/example_object.txt";

        // downloadFilePath 设置待下载的文件路径,建议使用绝对路径,确保路径下不存在文件,否则会将其覆盖。
        String downloadFilePath = "example_dir/example_file.txt";
        // taskNum 设置并发上传的并发数,范围为 1-1000
        int taskNum = 5;
        // partSize 设置文件分片大小,范围为 5MB - 5GB,默认为 20MB
        long partSize = 10 * 1024 * 1024;
        // enableCheckpoint 设置是否开启断点续传功能,开启则会在本地记录上传进度
        boolean enableCheckpoint = true;
        // checkpointFilePath 设置断点续传记录文件存放位置,若不设置则默认在 downloadFilePath 路径下生成
        // 其格式为 {downloadFilePath}.{bucket+objectKey+versionID 的 Base64Md5 值}.download
        String checkpointFilePath = "the checkpoint file path";

        TOSV2 tos = new TOSV2ClientBuilder().build(region, endpoint, accessKey, secretKey);

        try{
            HeadObjectV2Input head = new HeadObjectV2Input().setBucket(bucketName).setKey(objectKey);
            DownloadFileInput input = new DownloadFileInput().setHeadObjectV2Input(head)
                    .setFilePath(downloadFilePath).setEnableCheckpoint(enableCheckpoint)
                    .setCheckpointFile(checkpointFilePath).setPartSize(partSize).setTaskNum(taskNum);
            // 以下代码通过 DownloadEventListener 监听下载事件。
            // 如果出现 DownloadEventDownloadPartFailed 事件,即有下载失败的分片时就终止下载任务。
            // 以下代码仅作为示例,用户可根据业务需要进行使用。
            boolean isAbort = true;
            DownloadEventListener listener = new DownloadEventListener() {
                @Override
                public void eventChange(DownloadEvent downloadEvent) {
                    if (downloadEvent.getDownloadEventType() == DownloadEventType.DownloadEventDownloadPartFailed) {
                        System.out.println("event change, uploadPart failed");
                        if (input.getCancelHook() != null) {
                            // 调用 cancel 时,如果 isAbort 为 true,会终止断点续传,删除本地 checkpoint 文件,
                            // 并清理本地已下载的临时文件。
                            // 如果 isAbort 为 false,只会暂停当前下载任务,再次调用 downloadFile 可从断点处继续下载。
                            input.getCancelHook().cancel(isAbort);
                        }
                    }
                }
            };
            input.setCancelHook(true).setDownloadEventListener(listener);
            DownloadFileOutput output = tos.downloadFile(input);
            System.out.println("downloadFile succeed, object's etag is " + output.getEtag());
            System.out.println("downloadFile succeed, object's crc64 is " + output.getHashCrc64ecma());
        } catch (TosClientException e) {
            // 操作失败,捕获客户端异常,一般情况是请求参数错误,此时请求并未发送
            System.out.println("downloadFile failed");
            System.out.println("Message: " + e.getMessage());
            if (e.getCause() != null) {
                e.getCause().printStackTrace();
            }
        } catch (TosServerException e) {
            // 操作失败,捕获服务端异常,可以获取到从服务端返回的详细错误信息
            System.out.println("downloadFile failed");
            System.out.println("StatusCode: " + e.getStatusCode());
            System.out.println("Code: " + e.getCode());
            System.out.println("Message: " + e.getMessage());
            System.out.println("RequestID: " + e.getRequestID());
        } catch (Throwable t) {
            // 作为兜底捕获其他异常,一般不会执行到这里
            System.out.println("downloadFile failed");
            System.out.println("unexpected exception, message: " + t.getMessage());
        }
    }
}