当上传大文件时,经常出现因网络不稳定或程序崩溃导致上传失败的情况。失败后再次重新上传不仅浪费资源,而且当网络不稳定时仍然有上传失败的风险。断点续传上传接口uploadFile
能有效地解决此类问题引起的上传效率低下的问题。其原理是将待上传的文件分成若干个分片分别上传,如果出现网络异常或程序崩溃导致文件上传失败时,会将中断对象的断点处记录下来,从而能在失败重传时继续上传未上传完成的部分,节省资源提高效率,还因其能够对分片进行并发上传的机制能加快上传速度。
本文主要介绍通过Java SDK实现断点续传。SDK下载地址:SDK概览。
注意事项
断点续传上传,您必须必须是桶拥有者或拥有上传对象的权限,才能上传对象。
断点续传上传接口传入的文件大小至少要5MB以上,因为最小的分片大小就是5MB。
使用SDK的断点续传接口时,必须开启断点续传选项
setEnableCheckpoint
为true
才能在再次上传同一对象时读取到之前的上传进度。您可实现
ProgressListener
接口来实现对上传进度的监控。
示例代码
断点续传
以下为断点续传示例代码。
public class ResumeUploadDemo {
private static String endpoint = "https://gdoss.xstore.ctyun.cn";//资源池endpoint,示例以断广东资源池1区为例,其他资源池请根据实际情况填写
private static String accessKeyId = "ak";// 主账号或子账号ak
private static String accessKeySecret = "sk";// 主账号或子账号sk
private static String bucketName = "bucket";// 上传对象的目标bucket
private static String key = "xx.xx";// 对象名
private static String uploadFile = "xx.xx";// 本地待上传文件路径
public static void main(String[] args) {
ClientConfiguration clientConfiguration = new ClientConfiguration();
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);
AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(credentials);
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
endpoint, Regions.DEFAULT_REGION.getName());
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(credProvider)
.withClientConfiguration(clientConfiguration)
.withEndpointConfiguration(endpointConfiguration)
.withPathStyleAccessEnabled(false)
.build();
try {
// 通过UploadFileRequest设置多个参数。
// 依次填写Bucket名称以及对象名称。
UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, key);
// 指定监听器,当您实现以上接口后,可在这设置您的进度监控实例,从而完成对于上传进度的监控。
uploadFileRequest.setProgressListener(progressListener);
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
uploadFileRequest.setUploadFile(uploadFile);
// 指定上传并发线程数,默认值为1。
uploadFileRequest.setTaskNum(5);
// 指定上传的分片大小,单位为字节。默认值为5 MB。
uploadFileRequest.setPartSize(1024 * 1024 * 5);
// 开启断点续传,默认关闭。开启后,上传过程中的进度信息会保存在文件中,默认与待上传的本地文件同路径,名称为${uploadFile}.ucp,如果某一分片上传失败,再次上传时会根据文件中记录的点继续上传。上传完成后,该文件会被删除。
uploadFileRequest.setEnableCheckpoint(true);
try {
UploadFileResult uploadFileResult = s3Client.uploadFile(uploadFileRequest);
CompleteMultipartUploadResult multipartUploadResult = uploadFileResult.getMultipartUploadResult();
System.out.println(multipartUploadResult);
} catch (Throwable e) {
throw new RuntimeException(e);
}
} catch (Exception e) {
System.err.println("Upload failed: " + e.getMessage());
e.printStackTrace();
}
}
}
进度监控接口
通过实现上传过程监控接口,从而可以在上传过程中掌握您的大文件上传进度。常用的监听事件有:
REQUEST_CONTENT_LENGTH_EVENT(要在请求中发送的对象内容长度的事件)
TRANSFER_STARTED_EVENT(上传开始事件)
TRANSFER_PART_STARTED_EVENT(开始上传分片事件)
TRANSFER_PART_COMPLETED_EVENT(分片上传完毕事件)
TRANSFER_COMPLETED_EVENT(上传完毕事件)
TRANSFER_FAILED_EVENT(上传失败事件)
TRANSFER_PART_FAILED_EVENT(上传分片失败事件)
当有相应场景发生时progressChanged
,则会产生相应的事件回调,即可调用您的监控代码从而掌握对象的上传进度。
以下为进度监控代码。
ProgressListener progressListener = new ProgressListener() {
private long totalBytes = -1;
private long transferredBytes = 0;
private long startTime = System.currentTimeMillis();
private final Object lock = new Object(); // 用于线程安全更新进度
@Override
public void progressChanged(ProgressEvent event) {
switch (event.getEventType()) {
case REQUEST_CONTENT_LENGTH_EVENT:
totalBytes = event.getBytes();
System.out.printf("总大小: %,d bytes%n", totalBytes);
break;
case TRANSFER_STARTED_EVENT:
System.out.println("[开始] 文件传输启动");
startTime = System.currentTimeMillis();
break;
case CLIENT_REQUEST_SUCCESS_EVENT:
System.out.printf("[秒传] 文件已存在服务端 (大小: %.2fMB)%n", event.getBytes() / 1024.0 / 1024);
break;
case TRANSFER_PART_STARTED_EVENT:
long encodedStart = event.getBytes();
int partNumber = (int) (encodedStart >> 32);
long partSize = encodedStart & 0xFFFFFFFFL;
System.out.printf("[分片] #%d 开始上传 (大小: %.2fMB)%n",
partNumber, partSize / 1024.0 / 1024);
break;
case TRANSFER_PART_COMPLETED_EVENT:
long encodedComplete = event.getBytes();
long actualBytes = encodedComplete & 0xFFFFFFFFL;
synchronized (lock) {
transferredBytes += actualBytes;
}
printProgress();
break;
case TRANSFER_COMPLETED_EVENT:
System.out.println("\n[完成] 所有分片上传成功");
printFinalStats();
break;
case TRANSFER_FAILED_EVENT:
System.err.println("\n[失败] 文件传输异常终止");
printFinalStats();
break;
case TRANSFER_PART_FAILED_EVENT:
long encodedFail = event.getBytes();
int failedPart = (int) (encodedFail >> 32);
System.err.printf("[异常] 分片 #%d 上传失败%n", failedPart);
break;
}
}
private void printProgress() {
synchronized (lock) {
if (totalBytes <= 0)
return;
double percent = transferredBytes * 100.0 / totalBytes;
System.out.printf("\r[进度] %.2f%% - %.2fMB/%.2fMB",
percent,
transferredBytes / 1024.0 / 1024,
totalBytes / 1024.0 / 1024);
}
}
private void printFinalStats() {
long endTime = System.currentTimeMillis();
double elapsed = (endTime - startTime) / 1000.0;
double speed = (transferredBytes / 1024.0 / 1024) / elapsed;
System.out.printf("耗时: %.1fs | 平均速度: %.1fMB/s%n", elapsed, speed);
}
};