當上傳大文件時,經常出現因網絡不穩定或程序崩潰導致上傳失敗的情況。失敗后再次重新上傳不僅浪費資源,而且當網絡不穩定時仍然有上傳失敗的風險。斷點續傳上傳接口uploadFile能有效地解決此類問題引起的上傳效率低下的問題。其原理是將待上傳的文件分成若干個分片分別上傳,如果出現網絡異常或程序崩潰導致文件上傳失敗時,會將中斷對象的斷點處記錄下來,從而能在失敗重傳時繼續上傳未上傳完成的部分,節省資源提高效率,還因其能夠對分片進行并發上傳的機制能加快上傳速度。
本文主要介紹通過Java SDK實現斷點續傳。SDK下載地址:SDK概覽。
注意事項
斷點續傳上傳,您必須必須是桶擁有者或擁有上傳對象的權限,才能上傳對象。
斷點續傳上傳接口傳入的文件大小至少要5MB以上,因為最小的分片大小就是5MB。
使用SDK的斷點續傳接口時,必須開啟斷點續傳選項
setEnableCheckpoint為true才能在再次上傳同一對象時讀取到之前的上傳進度。您可實現
ProgressListener接口來實現對上傳進度的監控。
示例代碼
斷點續傳
以下為斷點續傳示例代碼。
public class ResumeUploadDemo {
private static String endpoint = "//gdoss.xstore.daliqc.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);
}
};