全部產品
Search
文件中心

Object Storage Service:下載到本地檔案(Java SDK V1)

更新時間:Nov 27, 2025

本文介紹如何將儲存空間(Bucket)中的檔案(Object)下載到本地檔案。

注意事項

  • 本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見地區和Endpoint

  • 本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見Java配置訪問憑證

  • 本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見常見情境配置樣本

許可權說明

阿里雲帳號預設擁有全部許可權。阿里雲帳號下的RAM使用者或RAM角色預設沒有任何許可權,需要阿里雲帳號或帳號管理員通過RAM PolicyBucket Policy授予操作許可權。

API

Action

說明

GetObject

oss:GetObject

下載Object。

oss:GetObjectVersion

下載Object時,如果通過versionId指定了Object的版本,則需要授予此操作的許可權。

kms:Decrypt

下載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樣本