本文介紹如何將儲存空間(Bucket)中的檔案(Object)下載到本地檔案。
注意事項
本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見地區和Endpoint。
本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見Java配置訪問憑證。
本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見常見情境配置樣本。
許可權說明
阿里雲帳號預設擁有全部許可權。阿里雲帳號下的RAM使用者或RAM角色預設沒有任何許可權,需要阿里雲帳號或帳號管理員通過RAM Policy或Bucket Policy授予操作許可權。
API | Action | 說明 |
GetObject |
| 下載Object。 |
| 下載Object時,如果通過versionId指定了Object的版本,則需要授予此操作的許可權。 | |
| 下載Object時,如果Object的中繼資料套件含X-Oss-Server-Side-Encryption: KMS,則需要此操作的許可權。 |
下載單個檔案到本地
以下代碼用於將examplebucket中testfolder目錄下的exampleobject.txt下載到本地D:\localpath路徑下的examplefile.txt。
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.GetObjectRequest;
import java.io.File;
public class Demo {
public static void main(String[] args) throws Exception {
// Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
//從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填寫Bucket名稱,例如examplebucket。
String bucketName = "examplebucket";
// 填寫不包含Bucket名稱在內的Object完整路徑,例如testfolder/exampleobject.txt。
String objectName = "testfolder/exampleobject.txt";
// 填寫Object下載到本地的完整路徑。
String pathName = "D:\\localpath\\examplefile.txt";
// 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。
String region = "cn-hangzhou";
// 建立OSSClient執行個體。
// 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 下載Object到本地檔案,並儲存到指定的本地路徑中。如果指定的本地檔案存在會覆蓋,不存在則建立。
// 如果未指定本地路徑,則下載後的檔案預設儲存到樣本程式所屬專案對應本地路徑中。
ossClient.getObject(new GetObjectRequest(bucketName, objectName), new File(pathName));
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
} 批量下載檔案到本地
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class BatchDownloadObject {
public static void main(String[] args) throws Exception {
// Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
//從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填寫Bucket名稱,例如examplebucket。
String bucketName = "examplebuckett";
// 填寫要下載的檔案夾首碼,例如"images/",如果要下載整個bucket則填寫""
String folderPrefix = "";
// 填寫本地下載目錄
String localDownloadPath = "./batch_downloads/";
// 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。
String region = "cn-hangzhou";
// 建立OSSClient執行個體。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 確保本地下載目錄存在
createDirectoryIfNotExists(localDownloadPath);
// 批量下載檔案
batchDownloadObjects(ossClient, bucketName, folderPrefix, localDownloadPath);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 批量下載OSS中的檔案到本地
* @param ossClient OSS用戶端
* @param bucketName 儲存桶名稱
* @param folderPrefix 檔案夾首碼
* @param localDownloadPath 本地下載路徑
*/
public static void batchDownloadObjects(OSS ossClient, String bucketName, String folderPrefix, String localDownloadPath) {
String nextMarker = null;
ObjectListing objectListing;
int downloadCount = 0;
long totalSize = 0;
int failedCount = 0;
System.out.println("開始批量下載檔案...");
System.out.println("源路徑: " + bucketName + "/" + folderPrefix);
System.out.println("本地路徑: " + localDownloadPath);
System.out.println("----------------------------------------");
long startTime = System.currentTimeMillis();
do {
// 列舉檔案
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
.withPrefix(folderPrefix)
.withMarker(nextMarker)
.withMaxKeys(1000); // 每次最多列舉1000個檔案
objectListing = ossClient.listObjects(listObjectsRequest);
List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
for (OSSObjectSummary s : sums) {
// 跳過檔案夾(以/結尾的對象)
if (s.getKey().endsWith("/")) {
continue;
}
try {
// 構造本地檔案路徑
String localFilePath = constructLocalFilePath(localDownloadPath, s.getKey(), folderPrefix);
// 建立本地檔案的父目錄
File localFile = new File(localFilePath);
createDirectoryIfNotExists(localFile.getParent());
// 檢查檔案是否已存在,避免重複下載
if (localFile.exists() && localFile.length() == s.getSize()) {
System.out.println("檔案已存在,跳過下載: " + s.getKey());
downloadCount++;
totalSize += s.getSize();
continue;
}
// 下載檔案
System.out.println("正在下載: " + s.getKey() + " -> " + localFilePath);
// 使用臨時檔案避免下載過程中的檔案損壞
File tempFile = new File(localFilePath + ".tmp");
try {
ossClient.getObject(new GetObjectRequest(bucketName, s.getKey()), tempFile);
// 下載完成後重新命名為最終檔案
if (tempFile.renameTo(localFile)) {
downloadCount++;
totalSize += s.getSize();
System.out.println("下載完成: " + s.getKey() + " (大小: " + formatFileSize(s.getSize()) + ")");
} else {
throw new IOException("無法重新命名臨時檔案: " + tempFile.getPath() + " -> " + localFile.getPath());
}
} finally {
// 清理臨時檔案
if (tempFile.exists()) {
tempFile.delete();
}
}
} catch (Exception e) {
failedCount++;
System.err.println("下載檔案失敗: " + s.getKey() + ", 錯誤: " + e.getMessage());
// 記錄詳細錯誤資訊用於調試
if (e instanceof OSSException) {
OSSException oe = (OSSException) e;
System.err.println("OSS錯誤碼: " + oe.getErrorCode() + ", 請求ID: " + oe.getRequestId());
}
}
}
nextMarker = objectListing.getNextMarker();
} while (objectListing.isTruncated());
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("----------------------------------------");
System.out.println("批量下載完成!");
System.out.println("總共下載檔案數: " + downloadCount);
System.out.println("下載失敗檔案數: " + failedCount);
System.out.println("總下載大小: " + formatFileSize(totalSize));
System.out.println("總耗時: " + formatDuration(duration));
System.out.println("平均下載速度: " + formatSpeed(totalSize, duration));
}
/**
* 構造本地檔案路徑
* @param localDownloadPath 本地下載根目錄
* @param objectKey OSS對象鍵
* @param folderPrefix 檔案夾首碼
* @return 本地檔案完整路徑
*/
private static String constructLocalFilePath(String localDownloadPath, String objectKey, String folderPrefix) {
// 移除首碼,保持相對路徑結構
String relativePath = objectKey;
if (folderPrefix != null && !folderPrefix.isEmpty() && objectKey.startsWith(folderPrefix)) {
relativePath = objectKey.substring(folderPrefix.length());
}
// 確保路徑分隔字元正確
String normalizedPath = localDownloadPath.endsWith(File.separator) ?
localDownloadPath : localDownloadPath + File.separator;
return normalizedPath + relativePath.replace("/", File.separator);
}
/**
* 建立目錄(如果不存在)
* @param dirPath 目錄路徑
*/
private static void createDirectoryIfNotExists(String dirPath) {
if (dirPath == null || dirPath.isEmpty()) {
return;
}
try {
File dir = new File(dirPath);
if (!dir.exists()) {
boolean created = dir.mkdirs();
if (created) {
System.out.println("建立目錄: " + dirPath);
} else if (!dir.exists()) {
throw new IOException("無法建立目錄: " + dirPath);
}
}
} catch (Exception e) {
System.err.println("建立目錄失敗: " + dirPath + ", 錯誤: " + e.getMessage());
throw new RuntimeException("建立目錄失敗", e);
}
}
/**
* 格式檔案大小
* @param size 檔案大小(位元組)
* @return 格式化後的檔案大小字串
*/
private static String formatFileSize(long size) {
if (size < 1024) {
return size + " B";
} else if (size < 1024 * 1024) {
return String.format("%.2f KB", size / 1024.0);
} else if (size < 1024 * 1024 * 1024) {
return String.format("%.2f MB", size / (1024.0 * 1024.0));
} else {
return String.format("%.2f GB", size / (1024.0 * 1024.0 * 1024.0));
}
}
/**
* 格式化時間長度
* @param duration 時間長度(毫秒)
* @return 格式化後的時間字串
*/
private static String formatDuration(long duration) {
long seconds = duration / 1000;
long minutes = seconds / 60;
long hours = minutes / 60;
if (hours > 0) {
return String.format("%d小時%d分鐘%d秒", hours, minutes % 60, seconds % 60);
} else if (minutes > 0) {
return String.format("%d分鐘%d秒", minutes, seconds % 60);
} else {
return String.format("%d秒", seconds);
}
}
/**
* 格式化下載速度
* @param totalBytes 總位元組數
* @param durationMs 期間(毫秒)
* @return 格式化後的速度字串
*/
private static String formatSpeed(long totalBytes, long durationMs) {
if (durationMs <= 0) {
return "N/A";
}
double bytesPerSecond = (double) totalBytes / (durationMs / 1000.0);
if (bytesPerSecond < 1024) {
return String.format("%.2f B/s", bytesPerSecond);
} else if (bytesPerSecond < 1024 * 1024) {
return String.format("%.2f KB/s", bytesPerSecond / 1024.0);
} else if (bytesPerSecond < 1024 * 1024 * 1024) {
return String.format("%.2f MB/s", bytesPerSecond / (1024.0 * 1024.0));
} else {
return String.format("%.2f GB/s", bytesPerSecond / (1024.0 * 1024.0 * 1024.0));
}
}
}
相關API
如果您的程式自訂要求較高,您可以直接發起REST API請求。直接發起REST API請求需要手動編寫代碼計算簽名。更多資訊,請參見GetObject。
相關文檔
關於下載到本地檔案的完整範例程式碼,請參見GitHub樣本。