全部產品
Search
文件中心

Object Storage Service:OSS Java SDK V1

更新時間:Oct 29, 2025

Github | OSS SDK for Java API | mvnrepository

快速接入

通過以下步驟快速接入 OSS Java SDK V1:

準備環境

安裝 Java 7 及以上版本。通過 java -version 命令查看 Java 版本,如當前環境沒有 Java 或版本低於 Java 7,請下載Java

安裝 SDK

根據開發環境選擇合適的安裝方式。建議使用最新版本的 OSS Java SDK V1,確保程式碼範例正常運行。

以安裝3.17.4版本的OSS Java SDK V1為例進行介紹。

添加Maven依賴(推薦)

在Maven專案中使用OSS Java SDK V1,只需在pom.xml中加入相應依賴。

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.17.4</version>
</dependency>

如使用Java 9及以上版本,需要添加以下JAXB相關依賴。

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.3</version>
</dependency>

在Eclipse專案中匯入JAR包

  1. 下載OSS Java SDK V1。

  2. 解壓開發包。

  3. 將解壓後檔案夾中的檔案aliyun-sdk-oss-3.17.4.jar以及lib檔案夾下的所有檔案拷貝到專案中。

  4. 在Eclipse中選擇工程,右鍵選擇Properties > Java Build Path > Add JARs

  5. 選中拷貝的所有JAR檔案,匯入到Libraries中。

在IntelliJ IDEA專案中匯入JAR包

  1. 下載OSS Java SDK V1 。

  2. 解壓開發包。

  3. 將解壓後檔案夾中的檔案aliyun-sdk-oss-3.17.4.jar以及lib檔案夾下的所有JAR檔案拷貝到專案中。

  4. 在IntelliJ IDEA中選擇工程,右鍵選擇File > Project Structure > Modules > Dependencies > + > JARs or directories

  5. 選中拷貝的所有JAR檔案,匯入到External Libraries中。

配置訪問憑證

使用 RAM 使用者的 AccessKey 配置訪問憑證。

  1. RAM 控制台,建立使用永久 AccessKey 訪問的 RAM 使用者,儲存 AccessKey,然後為該使用者授予 AliyunOSSFullAccess 許可權。

  2. 使用 RAM 使用者的 AccessKey 配置環境變數。

    Linux

    1. 在命令列介面執行以下命令,將環境變數設定追加到~/.bashrc 檔案中。

      echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.bashrc
      echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.bashrc
    2. 執行以下命令使變更生效。

      source ~/.bashrc
    3. 執行以下命令檢查環境變數是否生效。

      echo $OSS_ACCESS_KEY_ID
      echo $OSS_ACCESS_KEY_SECRET

    macOS

    1. 在終端中執行以下命令,查看預設Shell類型。

      echo $SHELL
    2. 根據預設Shell類型進行操作。

      Zsh

      1. 執行以下命令,將環境變數設定追加到 ~/.zshrc 檔案中。

        echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.zshrc
        echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.zshrc
      2. 執行以下命令使變更生效。

        source ~/.zshrc
      3. 執行以下命令檢查環境變數是否生效。

        echo $OSS_ACCESS_KEY_ID
        echo $OSS_ACCESS_KEY_SECRET

      Bash

      1. 執行以下命令,將環境變數設定追加到 ~/.bash_profile 檔案中。

        echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.bash_profile
        echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.bash_profile
      2. 執行以下命令使變更生效。

        source ~/.bash_profile
      3. 執行以下命令檢查環境變數是否生效。

        echo $OSS_ACCESS_KEY_ID
        echo $OSS_ACCESS_KEY_SECRET

    Windows

    CMD

    1. 在CMD中運行以下命令。

      setx OSS_ACCESS_KEY_ID "YOUR_ACCESS_KEY_ID"
      setx OSS_ACCESS_KEY_SECRET "YOUR_ACCESS_KEY_SECRET"
    2. 運行以下命令,檢查環境變數是否生效。

      echo %OSS_ACCESS_KEY_ID%
      echo %OSS_ACCESS_KEY_SECRET%

    PowerShell

    1. 在PowerShell中運行以下命令。

      [Environment]::SetEnvironmentVariable("OSS_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_ID", [EnvironmentVariableTarget]::User)
      [Environment]::SetEnvironmentVariable("OSS_ACCESS_KEY_SECRET", "YOUR_ACCESS_KEY_SECRET", [EnvironmentVariableTarget]::User)
    2. 運行以下命令,檢查環境變數是否生效。

      [Environment]::GetEnvironmentVariable("OSS_ACCESS_KEY_ID", [EnvironmentVariableTarget]::User)
      [Environment]::GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET", [EnvironmentVariableTarget]::User)

初始化用戶端

以下範例程式碼使用華東1(杭州)地區的外網訪問網域名稱初始化用戶端,並列舉該地區下的Bucket列表作為驗證。完整的地區和Endpoint列表請參見地區和Endpoint

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.Bucket;

import java.util.List;

/**
 * OSS SDK 快速接入樣本
 * 示範如何初始化 OSS 用戶端並列出所有 Bucket
 */
public class Test {

    public static void main(String[] args) {
        // 從環境變數擷取訪問憑證
        String accessKeyId = System.getenv("OSS_ACCESS_KEY_ID");
        String accessKeySecret = System.getenv("OSS_ACCESS_KEY_SECRET");

        // 設定OSS地區和Endpoint
        String region = "cn-hangzhou";
        String endpoint = "oss-cn-hangzhou.aliyuncs.com";

        // 建立憑證提供者
        DefaultCredentialProvider provider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);

        // 配置用戶端參數
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 顯式聲明使用V4簽名演算法
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);

        // 初始化OSS用戶端
        OSS ossClient = OSSClientBuilder.create()
                .credentialsProvider(provider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .endpoint(endpoint)
                .build();

        // 列出目前使用者的所有Bucket
        List<Bucket> buckets = ossClient.listBuckets();
        System.out.println("成功串連到OSS服務,當前帳號下的Bucket列表:");

        if (buckets.isEmpty()) {
            System.out.println("當前帳號下暫無Bucket");
        } else {
            for (Bucket bucket : buckets) {
                System.out.println("- " + bucket.getName());
            }
        }

        // 釋放資源
        ossClient.shutdown();
        System.out.println("OSS用戶端已關閉");
    }
}

運行後將顯示當前帳號在所有地區下的 Bucket 列表:

成功串連到OSS服務,當前帳號下的Bucket列表:
- example-bucket
OSS用戶端已關閉

用戶端配置

使用 ClientConfiguration 類配置 OSSClient 的逾時時間、重試次數、Proxy 伺服器等參數。

點擊查看用戶端支援的配置

方法

描述

ClientConfiguration.setMaxConnections

設定允許開啟的最大HTTP串連數。預設為1024。

ClientConfiguration.setSocketTimeout

設定Socket層傳輸資料的逾時時間(單位:毫秒)。預設為50000毫秒。

ClientConfiguration.setConnectionTimeout

設定建立串連的逾時時間(單位:毫秒)。預設為50000毫秒。

ClientConfiguration.setConnectionRequestTimeout

設定從串連池中擷取串連的逾時時間(單位:毫秒)。預設不逾時。

ClientConfiguration.setIdleConnectionTime

設定串連空閑逾時時間,逾時則關閉串連(單位:毫秒)。預設為60000毫秒。更多資訊,請參見阿里雲OSS Java SDK V1逾時時間設定

ClientConfiguration.setSupportCname

是否支援CNAME作為Endpoint,預設支援CNAME。

ClientConfiguration.setCrcCheckEnabled

是否支援開啟crc校正,預設開啟crc校正。

ClientConfiguration.setSLDEnabled

是否開啟次層網域(Second Level Domain)的訪問方式,預設不開啟。

ClientConfiguration.setProtocol

串連OSS所採用的協議(HTTP或HTTPS),預設為HTTP。

ClientConfiguration.setUserAgent

使用者代理程式,指HTTP的User-Agent頭。預設為aliyun-sdk-java

ClientConfiguration.setProxyHost

Proxy 伺服器主機地址。

ClientConfiguration.setProxyPort

Proxy 伺服器連接埠。

ClientConfiguration.setProxyUsername

Proxy 伺服器驗證的使用者名稱。

ClientConfiguration.setProxyPassword

Proxy 伺服器驗證的密碼。

ClientConfiguration.setRedirectEnable

是否開啟HTTP重新導向。

ClientConfiguration.setVerifySSLEnable

是否開啟SSL認證校正。

ClientConfiguration.setMaxErrorRetry

請求失敗後最大的重試次數。預設3次。

ClientConfiguration.setRetryStrategy

設定自訂重試策略,一般不建議設定。

使用自訂網域名

使用OSS預設網域名稱訪問時,由於OSS的安全性原則限制,可能會出現檔案禁止訪問、無法直接預覽等問題。通過綁定自訂網域名訪問OSS,不僅可以繞過這些訪問限制,實現檔案的直接預覽,還能結合CDN服務實現Global Acceleration分發,提升使用者訪問體驗。

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// 開啟CNAME選項以支援自訂網域名訪問
clientBuilderConfiguration.setSupportCname(true);

OSS ossClient = new OSSClientBuilder()
    // 其他配置...      
    .clientConfiguration(clientBuilderConfiguration)
    // 使用您的自訂網域名作為Endpoint,例如 https://static.example.com
    .endpoint("https://static.example.com")
    .build();

使用內網網域名稱

當應用程式部署在阿里雲ECS、Container Service或其他阿里雲產品上時,使用OSS內網網域名稱可以享受免費的內網流量傳輸,同時獲得更快的傳輸速度和更高的網路穩定性。內網訪問適用於大檔案上傳、批量資料處理和高頻訪問情境,有效降低成本並提升效能。完整的地區和內網Endpoint列表請參見地區和Endpoint

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();

OSS ossClient = new OSSClientBuilder()
    // 其他配置...      
    .clientConfiguration(clientBuilderConfiguration)
    // 使用內網網域名稱,以華東1(杭州)為例,其他地區請按實際情況填寫
    .endpoint("oss-cn-hangzhou-internal.aliyuncs.com")
    .build();

逾時控制

根據業務情境合理配置逾時參數。對於大檔案傳輸或網路環境不穩定的情境,建議適當增加逾時時間;對於高並發輕量級操作,可以設定較短的逾時時間以快速釋放資源。

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// 設定允許開啟的最大HTTP串連數,預設為1024
clientBuilderConfiguration.setMaxConnections(1024)
    // 設定Socket層傳輸資料的逾時時間(單位:毫秒),預設為50000毫秒
    .setSocketTimeout(50000)
    // 設定建立串連的逾時時間(單位:毫秒),預設為50000毫秒
    .setConnectionTimeout(50000)
    // 設定從串連池中擷取串連的逾時時間(單位:毫秒),預設無逾時限制
    .setConnectionRequestTimeout(60 * 60 * 24 * 1000)
    // 設定串連空閑逾時時間,逾時則關閉串連(單位:毫秒),預設為60000毫秒
    .setIdleConnectionTime(60000);

OSS ossClient = new OSSClientBuilder()
    // 其他配置...
    .clientConfiguration(clientBuilderConfiguration)
    .build();

最大錯誤重試次數

OSS用戶端預設重試3次以提升請求成功率。在高並發或網路不穩定環境下,適當使用 setMaxErrorRetry 增加重試次數可以顯著改善服務可用性,建議根據業務對延遲的容忍度和網路環境品質來調整重試次數。

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// 佈建要求失敗後的最大重試次數,根據網路環境和業務需求調整
clientBuilderConfiguration.setMaxErrorRetry(5);

OSS ossClient = new OSSClientBuilder()
    // 其他配置...
    .clientConfiguration(clientBuilderConfiguration)
    .build();

重試策略

不建議使用setRetryStrategy設定自訂重試策略,因為自訂策略可能導致不可預期的行為,影響服務穩定性。OSS用戶端針對不同請求類型採用經過驗證的重試策略:

  • POST 請求:預設不重試,避免重複提交導致的資料不一致。

  • 非 POST 請求:在滿足以下條件時自動重試,最多重試3次。

    • ClientException 異常,錯誤碼為:ConnectionTimeoutSocketTimeoutConnectionRefusedUnknownHostSocketException

    • OSSException 異常,錯誤碼非 InvalidResponse

    • HTTP 狀態代碼為 500、502 或 503 的服務端臨時錯誤。

Proxy 伺服器

商業網路環境通常通過Proxy 伺服器訪問外部資源以確保安全性。配置Proxy 伺服器後,OSS用戶端將通過指定的代理轉寄所有HTTP請求,實現安全的OSS服務訪問。

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();

// 設定使用者代理程式,指定HTTP的User-Agent頭,預設為aliyun-sdk-java
clientBuilderConfiguration.setUserAgent("aliyun-sdk-java");
// 設定Proxy 伺服器IP地址,請將""替換為實際的Proxy 伺服器IP(如"196.128.xxx.xxx")
clientBuilderConfiguration.setProxyHost("");
// 設定Proxy 伺服器訪問連接埠,例如8080連接埠
clientBuilderConfiguration.setProxyPort(8080);
// 設定Proxy 伺服器驗證的使用者名稱,請將""替換為實際的使用者名稱(如"admin")
clientBuilderConfiguration.setProxyUsername("");
// 設定Proxy 伺服器驗證的密碼,請將""替換為對應的驗證密碼
clientBuilderConfiguration.setProxyPassword("");

OSS ossClient = new OSSClientBuilder()
    // 其他配置...
    .clientConfiguration(clientBuilderConfiguration)
    .build();

HTTP/HTTPS 協議

通過 setProtocol 設定用戶端與OSS服務端的通訊協定。預設使用HTTP協議,生產環境強烈建議使用HTTPS協議以確保資料轉送安全,防止中間人攻擊和資料泄露。

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// 設定通訊協定為HTTPS,確保資料轉送安全
clientBuilderConfiguration.setProtocol(Protocol.HTTPS);

OSS ossClient = new OSSClientBuilder()
    // 其他配置...
    .clientConfiguration(clientBuilderConfiguration)
    .build();

簽名版本

重要

阿里雲Object Storage Service V1 簽名將按以下時間錶停止使用,建議儘快升級至 V4 簽名,確保服務不受影響。

  • 2025 年 3 月 1 日起,新註冊使用者無法使用 V1 簽名。

  • 2025 年 9 月 1 日起,逐步停止 V1 簽名的更新維護,且新建立的 Bucket 無法使用 V1 簽名。

使用 setSignatureVersion 配置簽名演算法版本。使用 V4 簽名時,必須通過 region 參數指定正確的地區ID。V4簽名演算法提供更高的安全性,OSS Java SDK V1 3.15.0 及以上版本支援 V4 簽名。

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// 設定簽名演算法版本為V4,提供更高的安全性
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);

OSS ossClient = new OSSClientBuilder()
    // 其他配置...
    // 使用V4簽名時必須指定地區ID
    .region("cn-hangzhou")
    .clientConfiguration(clientBuilderConfiguration)
    .build();

使用 IP 位址

使用IP地址作為Endpoint主要用於內網訪問和特殊網路環境情境。通過CEN、Express Connect、專線、VPN等方式建立網路連接後,可以使用IP地址直接存取OSS,繞過DNS解析過程提升訪問效率,具體路由配置可參考OSS內網網域名稱與VIP網段對照表

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// 開啟次層網域訪問OSS,預設情況下是關閉的
// OSS Java SDK V1 2.1.2及之前的版本需要設定此值
// 2.1.2及之後的版本會自動檢測到IP地址,因此不再需要設定此值
clientBuilderConfiguration.setSLDEnabled(true);

OSS ossClient = new OSSClientBuilder()
    // 其他配置...
    .clientConfiguration(clientBuilderConfiguration)
    // 使用IP地址訪問OSS時,建議使用HTTP協議以避免SSL認證校正問題
    .endpoint("http://10.10.10.10")
    .build();

CRC 校正

OSS預設開啟CRC資料校正功能以保證資料轉送完整性,雖然會略微增加傳輸時間,但建議在生產環境中保持開啟以確保資料一致性。以下特定情境可考慮關閉CRC校正以提升傳輸效能:直播預覽流、IoT裝置即時資料、低品質監控視頻等可容忍少量資料丟失的即時業務,或在高可靠內網環境下的批量資料轉送。關閉CRC校正前,請充分評估資料一致性風險並進行完整的測實驗證。

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// 關閉CRC資料校正功能,請謹慎使用並充分評估風險
clientBuilderConfiguration.setCrcCheckEnabled(false); 

OSS ossClient = new OSSClientBuilder()
    // 其他配置...
    .clientConfiguration(clientBuilderConfiguration)
    .build();

單例模式

OSS Java SDK V1 推薦採用單例模式來建立和使用 OSSClient 執行個體。

  • OSSClient 具備安全執行緒特性,支援多線程並發訪問同一執行個體。結合業務需求,採用單例模式建立並複用 OSSClient 執行個體,可以避免頻繁建立銷毀執行個體的開銷。

  • OSSClient 執行個體內部維護串連池,當確定 OSSClient 執行個體不再使用時,調用 shutdown 方法關閉執行個體,防止建立過多執行個體導致系統資源耗盡。

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import java.io.ByteArrayInputStream;

public class OssClientSingleton {
    private OssClientSingleton() {}

    // 靜態內部類實現單例(安全執行緒)
    private static class SingletonHolder {
        private static final OSS INSTANCE = create();

        private static OSS create() {
            try {
                // Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
                String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
                // 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。
                String region = "cn-hangzhou";

                ClientBuilderConfiguration config = new ClientBuilderConfiguration();
                // 顯式聲明使用 V4 簽名演算法
                config.setSignatureVersion(SignVersion.V4);

                //從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
                CredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

                // 構建OSS用戶端
                return OSSClientBuilder.create()
                        .endpoint(endpoint)
                        .credentialsProvider(credentialsProvider)
                        .clientConfiguration(config)
                        .region(region)
                        .build();
            } catch (Exception e) {
                throw new RuntimeException("OSS用戶端初始化失敗", e);
            }
        }
    }

    // 擷取單例執行個體
    public static OSS getInstance() {
        return SingletonHolder.INSTANCE;
    }

    // 主函數測試PutObject操作
    public static void main(String[] args) {
        // 擷取單例OSS用戶端
        OSS ossClient = OssClientSingleton.getInstance();

        // 填寫Bucket名稱,例如examplebucket。
        String bucketName = "examplebucket";
        // 填寫不包含Bucket名稱在內的Object完整路徑,例如testfolder/exampleobject.txt。
        String objectKey = "testfolder/exampleobject.txt";

        try {
            // 填寫字串。
            String content = "Hello OSS";

            // 建立PutObjectRequest對象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, new ByteArrayInputStream(content.getBytes()));

            // 如果需要上傳時設定儲存類型和存取權限,請參考以下範例程式碼。
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            metadata.setObjectAcl(CannedAccessControlList.Private);
            putObjectRequest.setMetadata(metadata);

            // 上傳字串。
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            // 列印上傳結果。
            System.out.println("檔案上傳成功!");
            System.out.println("ETag: " + result.getETag());
            System.out.println("請求ID: " + result.getRequestId());
        } 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 {
            // 在單例模式下,不建議在每次操作後關閉client(保持串連複用),避免影響後續使用。
            // 在明確OSSClient執行個體不再使用時(例如應用程式退出前),調用一次shutdown方法以釋放資源。
            // ossClient.shutdown();
        }
    }
}

異常處理

OSS Java SDK V1包含兩類異常:ClientException和OSSException,均繼承自RuntimeException。

用戶端異常(ClientException)

用戶端異常指在構建請求、發送請求或傳輸資料過程中發生的異常。常見情境包括:

  • 網路連接不可用,導致請求無法發送到服務端。

  • 上傳檔案時發生 IO 異常。

  • 請求逾時、認證驗證失敗等底層網路異常。

拋出ClientException表示請求未成功發送至OSS服務端,或在用戶端處理過程中出現錯誤,通常需要檢查網路連接狀態、用戶端配置或重試機制。

服務端異常(OSSException)

服務端異常表示OSS服務端返回的異常資訊,即請求已成功發送並被服務端接收,但由於某些原因未能正常處理。OSS服務端異常具有以下特點:

  • 包含詳細的錯誤碼(ErrorCode)和錯誤資訊(ErrorMessage),便於精確定位問題。

  • 常見錯誤包括簽名不匹配(SignatureDoesNotMatch)、許可權不足(AccessDenied)、資源不存在(NoSuchKey)等。

  • 通過錯誤碼進行針對性處理,可以顯著提升程式的健壯性和使用者體驗。

在實際開發中建議對這兩類異常分別進行捕獲,以便更準確地判斷問題來源並制定相應的處理策略。

// 建立用戶端的操作...

try {
    // 執行OSS操作,例如上傳檔案、下載檔案、列舉對象等
    // 這裡的操作可能會拋出OSS相關的異常
    ossClient.putObject(...);
    
} catch (OSSException oe) {
    // 捕獲OSS服務端返回的異常
    // 當請求成功到達OSS服務端,但服務端拒絕執行請求時拋出此異常
    // 常見原因:許可權不足、參數錯誤、資源不存在等
    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) {
    // 捕獲用戶端異常
    // 當用戶端在與OSS通訊過程中遇到嚴重內部問題時拋出此異常
    // 常見原因:網路連接問題、SSL認證問題、DNS解析失敗等
    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 Java SDK V1提供豐富的範例程式碼,涵蓋儲存空間管理、檔案操作、許可權控制、加密傳輸等核心功能,便於參考或直接使用。範例程式碼包括以下內容:

樣本檔案

樣本內容

BucketOperationsSample.java

BucketTaggingSample.java

儲存空間標籤(Java SDK)

BucketInventorySample.java

儲存空間清單(Java SDK)

BucketPolicySample.java

Bucket Policy(Java SDK)

BucketWormSample.java

合規保留原則(Java SDK)

SetRequestPaymentSample.java

要求者付費模式(Java SDK)

BucketReplicationSample.java

資料複製(Java SDK)

BucketTransferAccelerationSample.java

傳輸加速(Java SDK)

CreateFolderSample.java

簡單上傳(Java SDK)

PostObjectSample.java

表單上傳(Java SDK)

說明

PostObject的實現不依賴Java SDK。

AppendObjectSample.java

追加上傳(Java SDK)

UploadSample.java

斷點續傳上傳(Java SDK)

MultipartUploadSample.java

分區上傳(Java SDK)

CallbackSample.java

上傳回調(Java SDK)

SimpleGetObjectSample.java

下載檔案(Java SDK)

DownloadSample.java

斷點續傳下載(Java SDK)

ConcurrentGetObjectSample.java

斷點續傳下載(Java SDK)

GetProgressSample.java

GetStartedSample.java

ObjectMetaSample.java

管理檔案中繼資料(Java SDK)

StorageTypeSample.java

轉換檔儲存類型(Java SDK)

ListObjectsSample.java

列舉檔案(Java SDK)

BucketMetaQuerySample.java

標量檢索(Java SDK)

SelectObjectSample.java

查詢檔案(Java SDK)

DeleteObjectsSample.java

刪除檔案(Java SDK)

UploadPartCopySample.java

拷貝檔案(Java SDK)

ObjectOperationSample.java

禁止覆蓋同名檔案(Java SDK)

RestoreObjectSample.java

解凍檔案(Java SDK)

SymLinkSample.java

管理軟連結(Java SDK)

BucketVersioningSample.java

管理版本控制(Java SDK)

ObjectTaggingSample.java

TrafficLimitSample.java

單連結限速(Java SDK)

用戶端加密(Java SDK)

EncryptionServiceSample.java

伺服器端加密(Java SDK)

AuthorizedAccessSample.java

使用預簽名URL下載(Java SDK)

CRCSample.java

資料校正(Java SDK)

ImageSample.java

圖片處理(Java SDK)

訪問憑證配置

OSS 支援多種憑證初始化方式,需根據認證和授權需求選擇合適的初始化方式。

如何選擇訪問憑證?

憑證提供者初始化方式

適用情境

是否需要提供前置的AK或STS Token

底層依賴的憑證

憑證有效期間

憑證輪轉或重新整理方式

使用RAM使用者的AK

部署運行在安全、穩定且不易受外部攻擊的環境的應用程式,無需頻繁輪轉憑證就可以長期訪問雲端服務

AK

長期

手動輪轉

使用STS臨時訪問憑證

部署運行在不可信的環境的應用程式,希望能控制訪問的有效期間、許可權

STS Token

臨時

手動重新整理

使用RAMRoleARN

需要授權訪問雲端服務,例如跨阿里雲帳號訪問雲端服務的應用程式

STS Token

臨時

自動重新整理

使用ECSRAMRole

部署運行在阿里雲的ECS執行個體、ECI執行個體、Container ServiceKubernetes版的Worker節點中的應用程式

STS Token

臨時

自動重新整理

使用OIDCRoleARN

部署運行在阿里雲的Container ServiceKubernetes版的Worker節點中的不可信應用程式

STS Token

臨時

自動重新整理

使用Function Compute上下文中的Credentials

部署運行在阿里雲Function Compute中的應用程式的函數

STS Token

臨時

無需重新整理

使用CredentialsURI

需要通過外部系統擷取訪問憑證的應用程式

STS Token

臨時

自動重新整理

使用自動輪轉的AK

部署運行在面臨AK泄露風險的環境的應用程式,需要頻繁輪轉憑證才能長期訪問雲端服務

AK

長期

自動輪轉

使用自訂訪問憑證

當以上憑證配置方式都不滿足要求時,可通過自訂擷取憑證的方式實現

自訂

AK或STS Token

自訂

自訂

使用預設憑據鏈

初始化憑據用戶端時不傳入任何參數,Credentials工具將使用預設憑據鏈方式初始化用戶端

AK或STS Token

自訂

自動重新整理

使用RAM使用者的AK

適用於應用程式部署運行在安全、穩定且不易受外部攻擊的環境中,需要長期訪問OSS且無法頻繁輪轉憑證的情境。通過阿里雲主帳號或RAM使用者的AK(Access Key ID、Access Key Secret)初始化憑證提供者。該方式需要手動維護AK,存在安全性風險和維護複雜度增加的風險。

重要
  • 阿里雲帳號擁有資源的全部許可權,AK一旦泄露,會給系統帶來巨大風險,不建議使用。推薦使用最小化授權的RAM使用者的AK。

  • 如需建立RAM使用者的AK,請直接存取建立AccessKey。RAM使用者的Access Key ID、Access Key Secret資訊僅在建立時顯示,如若遺忘請建立新的AK進行替換。

環境變數

  1. 使用RAM使用者AccessKey配置環境變數。

    Linux

    1. 在命令列介面執行以下命令,將環境變數設定追加到~/.bashrc 檔案中。

      echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.bashrc
      echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.bashrc
    2. 執行以下命令使變更生效。

      source ~/.bashrc
    3. 執行以下命令檢查環境變數是否生效。

      echo $OSS_ACCESS_KEY_ID
      echo $OSS_ACCESS_KEY_SECRET

    macOS

    1. 在終端中執行以下命令,查看預設Shell類型。

      echo $SHELL
    2. 根據預設Shell類型進行操作。

      Zsh

      1. 執行以下命令,將環境變數設定追加到 ~/.zshrc 檔案中。

        echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.zshrc
        echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.zshrc
      2. 執行以下命令使變更生效。

        source ~/.zshrc
      3. 執行以下命令檢查環境變數是否生效。

        echo $OSS_ACCESS_KEY_ID
        echo $OSS_ACCESS_KEY_SECRET

      Bash

      1. 執行以下命令,將環境變數設定追加到 ~/.bash_profile 檔案中。

        echo "export OSS_ACCESS_KEY_ID='YOUR_ACCESS_KEY_ID'" >> ~/.bash_profile
        echo "export OSS_ACCESS_KEY_SECRET='YOUR_ACCESS_KEY_SECRET'" >> ~/.bash_profile
      2. 執行以下命令使變更生效。

        source ~/.bash_profile
      3. 執行以下命令檢查環境變數是否生效。

        echo $OSS_ACCESS_KEY_ID
        echo $OSS_ACCESS_KEY_SECRET

    Windows

    CMD

    1. 在CMD中運行以下命令。

      setx OSS_ACCESS_KEY_ID "YOUR_ACCESS_KEY_ID"
      setx OSS_ACCESS_KEY_SECRET "YOUR_ACCESS_KEY_SECRET"
    2. 運行以下命令,檢查環境變數是否生效。

      echo %OSS_ACCESS_KEY_ID%
      echo %OSS_ACCESS_KEY_SECRET%

    PowerShell

    1. 在PowerShell中運行以下命令。

      [Environment]::SetEnvironmentVariable("OSS_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_ID", [EnvironmentVariableTarget]::User)
      [Environment]::SetEnvironmentVariable("OSS_ACCESS_KEY_SECRET", "YOUR_ACCESS_KEY_SECRET", [EnvironmentVariableTarget]::User)
    2. 運行以下命令,檢查環境變數是否生效。

      [Environment]::GetEnvironmentVariable("OSS_ACCESS_KEY_ID", [EnvironmentVariableTarget]::User)
      [Environment]::GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET", [EnvironmentVariableTarget]::User)
  2. 參考上述方式修改系統內容變數後,需重啟或重新整理編譯運行環境,包括IDE、命令列介面、其他傳統型應用程式及後台服務,以確保最新的系統內容變數成功載入。

  3. 使用環境變數傳遞憑證資訊。

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class AkDemoTest {
        public static void main(String[] args) throws Exception {
            // 從環境變數中擷取憑證
            EnvironmentVariableCredentialsProvider credentialsProvider =  CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // 使用credentialsProvider進行後續操作...
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);   
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
                    
            ossClient.shutdown();
        }
    }

靜態憑證

以下範例程式碼展示了對訪問憑據直接進行寫入程式碼,顯式設定要使用的存取金鑰的方法。

重要

請勿將訪問憑據嵌入到生產環境的應用程式中,此方法僅用於測試目的。

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.comm.SignVersion;

public class AkDemoTest {
    public static void main(String[] args) throws Exception {
        // 填寫RAM使用者的Access Key ID和Access Key Secret
        String accessKeyId = "yourAccessKeyID";
        String accessKeySecret = "yourAccessKeySecret";
        
        // 使用DefaultCredentialProvider方法直接設定AK和SK
        CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);

        // 使用credentialsProvider初始化用戶端
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 顯式聲明使用 V4 簽名演算法
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
        // 建立OSSClient執行個體。
        // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
        OSS ossClient = OSSClientBuilder.create()
                .endpoint("endpoint")
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region("region")
                .build();

        ossClient.shutdown();

    }
}

使用STS臨時訪問憑證

適用於應用程式需要臨時訪問OSS的情境。通過STS服務擷取的臨時身份憑證(Access Key ID、Access Key Secret和Security Token)初始化憑證提供者。該方式需要手動維護STS Token,存在安全性風險和維護複雜度增加的風險。如果需要多次臨時訪問OSS,需要手動重新整理STS Token。

重要

環境變數

  1. 使用臨時身份憑證設定環境變數。

    Mac OS/Linux/Unix

    重要
    • 請注意,此處使用的是通過STS服務擷取的臨時身份憑證(Access Key ID、Access Key Secret和Security Token),而非RAM使用者的Access Key和Access Key Secret。

    • 請注意區分STS服務擷取的Access Key ID以STS開頭,例如“STS.****************”。

    export OSS_ACCESS_KEY_ID=<STS_ACCESS_KEY_ID>
    export OSS_ACCESS_KEY_SECRET=<STS_ACCESS_KEY_SECRET>
    export OSS_SESSION_TOKEN=<STS_SECURITY_TOKEN>

    Windows

    重要
    • 請注意,此處使用的是通過STS服務擷取的臨時身份憑證(Access Key ID、Access Key Secret和Security Token),而非RAM使用者的AK(Access Key ID、Access Key Secret)。

    • 請注意區分STS服務擷取的Access Key ID以STS開頭,例如“STS.****************”。

    set OSS_ACCESS_KEY_ID=<STS_ACCESS_KEY_ID>
    set OSS_ACCESS_KEY_SECRET=<STS_ACCESS_KEY_SECRET>
    set OSS_SESSION_TOKEN=<STS_SECURITY_TOKEN>
  2. 通過環境變數傳遞憑證資訊。

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class StsDemoTest {
        public static void main(String[] args) throws Exception {
            // 從環境變數中擷取憑證
            EnvironmentVariableCredentialsProvider credentialsProvider =  CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            
            // 使用credentialsProvider初始化用戶端
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
    
            ossClient.shutdown();
        }
    }

靜態憑證

可以在應用程式中對憑據直接進行寫入程式碼,顯式設定要使用的臨時存取金鑰。

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.comm.SignVersion;

public class StsDemoTest {
    public static void main(String[] args) throws Exception {
        // 請設定為您通過STS服務擷取的臨時身份憑證Access Key ID、Access Key Secret和Security Token,而非RAM使用者的身份憑證資訊
        // 請注意區分STS服務擷取的Access Key ID是以STS開頭,如下所示
        String accessKeyId = "STS.****************";
        String accessKeySecret = "yourAccessKeySecret";
        String stsToken= "yourSecurityToken";

        // 使用DefaultCredentialProvider方法直接設定AK和SK
        CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret, stsToken);

        // 使用credentialsProvider初始化用戶端
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 顯式聲明使用 V4 簽名演算法
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        // 建立OSSClient執行個體。
        // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
        OSS ossClient = OSSClientBuilder.create()
                .endpoint("endpoint")
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region("region")
                .build();

        ossClient.shutdown();
    }
}

使用RAMRoleARN

適用於應用程式需要授權訪問OSS,例如跨阿里雲帳號訪問OSS的情境。通過指定RAM角色的ARN(Alibabacloud Resource Name)初始化憑證提供者,底層實現基於STS Token。Credentials工具會前往STS服務擷取STS Token,並在會話到期前調用AssumeRole介面申請新的STS Token。還可以通過為policy賦值來限制RAM角色到一個更小的許可權集合。

重要
  • 阿里雲帳號擁有資源的全部許可權,AK一旦泄露,會給系統帶來巨大風險,不建議使用。推薦使用最小化授權的RAM使用者的AK。

  • 如需建立RAM使用者的AK,請直接存取建立AccessKey。RAM使用者的Access Key ID、Access Key Secret資訊僅在建立時顯示,請及時儲存,如若遺忘請建立新的AK進行輪換。

  • 如需擷取RAMRoleARN,請直接存取CreateRole - 建立角色

  1. 添加credentials依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun/credentials-java -->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>credentials-java</artifactId>
        <version>LATEST</version>
    </dependency>
  2. 配置AK和RAMRoleARN作為訪問憑證。

    import com.aliyun.credentials.models.CredentialModel;
    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.Credentials;
    import com.aliyun.oss.common.auth.CredentialsProvider;
    import com.aliyun.oss.common.auth.DefaultCredentials;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class RamRoleArnAkDemoTest {
    
        public static void main(String[] args) {
    
            com.aliyun.credentials.models.Config config = new com.aliyun.credentials.models.Config();
            // 訪問憑證類型。固定為ram_role_arn
            config.setType("ram_role_arn");
            // 要扮演的RAM角色ARN,樣本值:acs:ram::123456789012****:role/adminrole,可以通過環境變數ALIBABA_CLOUD_ROLE_ARN設定RoleArn
            config.setRoleArn("<RoleArn>");
            // 從環境變數中擷取AccessKeyId
            config.setAccessKeyId(System.getenv().get("ALIBABA_CLOUD_ACCESS_KEY_ID"));
            // 從環境變數中擷取AccessKeySecret
            config.setAccessKeySecret(System.getenv().get("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
            // 角色會話名稱,可以通過環境變數ALIBABA_CLOUD_ROLE_SESSION_NAME設定RoleSessionName
            config.setRoleName("<RoleSessionName>");
            // 設定更小的權限原則,非必填。樣本值:{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}
            config.setPolicy("<Policy>");
            // 設定角色會話有效期間,非必填
            config.setRoleSessionExpiration(3600);
    
            final com.aliyun.credentials.Client credentialsClient = new com.aliyun.credentials.Client(config);
    
            CredentialsProvider credentialsProvider = new CredentialsProvider(){
                @Override
                public void setCredentials(Credentials credentials) {
                }
    
                @Override
                public Credentials getCredentials() {
                    CredentialModel credential = credentialsClient.getCredential();
                    return  new DefaultCredentials(credential.getAccessKeyId(), credential.getAccessKeySecret(), credential.getSecurityToken());
                }
            };
            // 使用credentialsProvider初始化用戶端
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
    
            ossClient.shutdown();
        }
    }

使用ECSRAMRole

適用於應用程式運行在ECS執行個體、ECI執行個體、Container ServiceKubernetes版的Worker節點中的情境。建議使用ECSRAMRole初始化憑證提供者,底層實現基於STS Token。ECSRAMRole允許將一個角色關聯到ECS執行個體、ECI執行個體或Container Service Kubernetes 版的Worker節點,實現在執行個體內部自動重新整理STS Token。該方式無需提供AK或STS Token,消除了手動維護AK或STS Token的風險。如何擷取ECSRAMRole,請參見CreateRole - 建立角色。如何將一個角色關聯到ECS執行個體,請參見執行個體RAM角色

  1. 添加credentials依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun/credentials-java -->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>credentials-java</artifactId>
        <version>LATEST</version>
    </dependency>
  2. 配置ECSRAMRole作為訪問憑證。

    import com.aliyun.credentials.models.CredentialModel;
    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.Credentials;
    import com.aliyun.oss.common.auth.CredentialsProvider;
    import com.aliyun.oss.common.auth.DefaultCredentials;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class EcsRamRoleDemoTest {
        public static void main(String[] args) {
    
            com.aliyun.credentials.models.Config config = new com.aliyun.credentials.models.Config();
            // 訪問憑證類型。固定為ecs_ram_role。
            config.setType("ecs_ram_role");
            // 為ECS執行個體分配的RAM角色名稱。
            config.setRoleName("<RoleName>");
    
            final com.aliyun.credentials.Client credentialsClient = new com.aliyun.credentials.Client(config);
    
            CredentialsProvider credentialsProvider = new CredentialsProvider(){
                @Override
                public void setCredentials(Credentials credentials) {
                }
    
                @Override
                public Credentials getCredentials() {
                    CredentialModel credential = credentialsClient.getCredential();
                    return  new DefaultCredentials(credential.getAccessKeyId(), credential.getAccessKeySecret(), credential.getSecurityToken());
                }
            };
            // 使用credentialsProvider初始化用戶端
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
    
            ossClient.shutdown();
        }
    }

使用OIDCRoleARN

在Container ServiceKubernetes版中設定了Worker節點RAM角色後,對應節點內的Pod中的應用也可以像ECS上部署的應用一樣,通過中繼資料服務(Meta Data Server)擷取關聯角色的STS Token。但如果容器叢集上部署的是不可信的應用(比如部署客戶提交的應用,代碼也沒有開放),可能並不希望它們能通過中繼資料服務擷取Worker節點關聯執行個體RAM角色的STS Token。為了避免影響雲上資源的安全,同時又能讓這些不可信的應用安全地擷取所需的STS Token,實現應用層級的許可權最小化,可以使用RRSA(RAM Roles for Service Account)功能。該方式底層實現基於STS Token。阿里雲容器叢集會為不同的應用Pod建立和掛載相應的服務賬戶OIDC Token檔案,並將相關配置資訊注入到環境變數中,Credentials工具通過擷取環境變數的配置資訊,調用STS服務的AssumeRoleWithOIDC介面換取綁定角色的STS Token。該方式無需提供AK或STS Token,消除了手動維護AK或STS Token的風險。詳情請參見基於RRSA的Pod許可權隔離

  1. 添加credentials依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun/credentials-java -->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>credentials-java</artifactId>
        <version>LATEST</version>
    </dependency>
  2. 配置OIDC的RAM角色作為訪問憑證。

    import com.aliyun.credentials.models.CredentialModel;
    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.Credentials;
    import com.aliyun.oss.common.auth.CredentialsProvider;
    import com.aliyun.oss.common.auth.DefaultCredentials;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class OidcRoleArnDemoTest {
        public static void main(String[] args) {
    
            com.aliyun.credentials.models.Config config = new com.aliyun.credentials.models.Config();
            // 指定Credential類型,固定值為oidc_role_arn
            config.setType("oidc_role_arn");
            // RAM角色名稱ARN,可以通過環境變數ALIBABA_CLOUD_ROLE_ARN設定RoleArn
            config.setRoleArn("<RoleArn>");
            // OIDC供應商ARN,可以通過環境變數ALIBABA_CLOUD_OIDC_PROVIDER_ARN設定OidcProviderArn
            config.setOidcProviderArn("<OidcProviderArn>");
            // OIDC Token檔案路徑,可以通過環境變數ALIBABA_CLOUD_OIDC_TOKEN_FILE設定OidcTokenFilePath
            config.setOidcTokenFilePath("<OidcTokenFilePath>");
            // 角色會話名稱,可以通過環境變數ALIBABA_CLOUD_ROLE_SESSION_NAME設定RoleSessionName
            config.setRoleSessionName("<RoleSessionName>");
            // 設定更小的權限原則,非必填。樣本值:{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}
            config.setPolicy("<Policy>");
            // 設定session到期時間
            config.setRoleSessionExpiration(3600);
    
            final com.aliyun.credentials.Client credentialsClient = new com.aliyun.credentials.Client(config);
    
            CredentialsProvider credentialsProvider = new CredentialsProvider(){
                @Override
                public void setCredentials(Credentials credentials) {
                }
    
                @Override
                public Credentials getCredentials() {
                    CredentialModel credential = credentialsClient.getCredential();
                    return  new DefaultCredentials(credential.getAccessKeyId(), credential.getAccessKeySecret(), credential.getSecurityToken());
                }
            };
            // 使用credentialsProvider初始化用戶端
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
    
            ossClient.shutdown();
        }
    }

使用Function Compute上下文中的Credentials

適用於應用程式的函數部署運行在Function Compute中的情境。可以使用Function Compute上下文中的Credentials初始化憑證提供者,底層實現基於STS Token。Function Compute根據函數配置的角色,通過扮演服務角色擷取一個STS Token,然後通過上下文中的參數Credentials將STS Token傳遞給應用程式。該STS Token的有效期間為36小時,且不支援修改。函數的最大執行時間為24小時,因此,執行函數過程中,STS Token不會到期,無需考慮重新整理問題。該方式無需提供AK或STS Token,消除了手動維護AK或STS Token的風險。如何授予Function Compute訪問OSS的許可權,請參見使用函數角色授予Function Compute訪問其他雲端服務的許可權

  1. 添加Function Compute上下文依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun.fc.runtime/fc-java-core -->
    <dependency>
        <groupId>com.aliyun.fc.runtime</groupId>
        <artifactId>fc-java-core</artifactId>
        <version>1.4.1</version>
    </dependency>
  2. 使用Function Compute上下文中的Credentials初始化憑證提供者。

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    import com.aliyun.fc.runtime.Context;
    import com.aliyun.fc.runtime.Credentials;
    import com.aliyun.fc.runtime.StreamRequestHandler;
    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class App implements StreamRequestHandler {
    
        @Override
        public void handleRequest(
            InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
    
            // 擷取密鑰資訊,執行前,確保函數所在的服務配置了角色資訊,並且角色需要擁有相關OSS許可權,建議直接使用AliyunFCDefaultRole角色
            Credentials creds = context.getExecutionCredentials();
    
            // 使用擷取到的憑證建立憑證提供者執行個體
            CredentialsProvider credentialsProvider = new DefaultCredentialProvider(creds.getAccessKeyId(), creds.getAccessKeySecret(), creds.getSecurityToken());
    
            // 使用credentialsProvider初始化用戶端
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
    
            ossClient.shutdown();
    
            outputStream.write(new String("done").getBytes());
        }
    }

使用CredentialsURI

適用於應用程式需要通過外部系統擷取阿里雲憑證,從而實現靈活的憑證管理和無密鑰訪問的情境。可以使用CredentialsURI初始化憑證提供者,底層實現基於STS Token。Credentials工具通過提供的URI擷取STS Token,完成憑證用戶端初始化。該方式無需提供AK或STS Token,消除了手動維護AK或STS Token的風險。

  1. 為了使Credentials工具正確解析和使用STS Token,URI必須遵循以下響應協議:

    • 響應狀態代碼:200

    • 響應體結構:

      {
          "Code": "Success",
          "AccessKeySecret": "AccessKeySecret",
          "AccessKeyId": "AccessKeyId",
          "Expiration": "2021-09-26T03:46:38Z",
          "SecurityToken": "SecurityToken"
      }
  2. 添加credentials依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun/credentials-java -->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>credentials-java</artifactId>
        <version>LATEST</version>
    </dependency>
  3. 配置CredentialsURI作為訪問憑證。

    import com.aliyun.credentials.models.CredentialModel;
    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.Credentials;
    import com.aliyun.oss.common.auth.CredentialsProvider;
    import com.aliyun.oss.common.auth.DefaultCredentials;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class CredentialsUriDemoTest {
        public static void main(String[] args) {
    
            com.aliyun.credentials.models.Config config = new com.aliyun.credentials.models.Config();
            // 訪問憑證類型。固定為credentials_uri
            config.setType("credentials_uri");
            // 憑證的 URI,即您產生STS Token的伺服器位址,格式為http://local_or_remote_uri/,可以通過環境變數ALIBABA_CLOUD_CREDENTIALS_URI設定CredentialsUri
            config.setCredentialsUri("<CredentialsUri>");
    
            final com.aliyun.credentials.Client credentialsClient = new com.aliyun.credentials.Client(config);
    
            CredentialsProvider credentialsProvider = new CredentialsProvider(){
                @Override
                public void setCredentials(Credentials credentials) {
                }
    
                @Override
                public Credentials getCredentials() {
                    CredentialModel credential = credentialsClient.getCredential();
                    return  new DefaultCredentials(credential.getAccessKeyId(), credential.getAccessKeySecret(), credential.getSecurityToken());
                }
            };
            // 使用credentialsProvider初始化用戶端
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
    
            ossClient.shutdown();
        }
    }

使用自動輪轉的AK

適用於應用程式需要長期訪問OSS,但部署啟動並執行環境面臨AK泄露風險,需要頻繁手動輪轉(輪換)AK的情境。可以使用ClientKey初始化憑證提供者,底層實現基於AK。使用ClientKey後,Key Management Service(KMS)可以對託管的RAM使用者AK進行全自動的定期輪轉,將靜態RAM使用者AK動態化,從而降低AK泄漏的風險。除定期輪轉外,KMS還支援立即輪轉,在AK泄漏情況下快速更換AK。該方式無需手動維護AK,從而降低安全性風險和維護複雜度。如何擷取ClientKey,請參見建立應用存取點

  1. 添加憑據用戶端依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun/alibabacloud-secretsmanager-client -->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>alibabacloud-secretsmanager-client</artifactId>
        <version>1.3.7</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core -->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-core</artifactId>
        <version>4.7.0</version>
    </dependency>
  2. 建立設定檔secretsmanager.properties

    # 訪問憑據類型,固定為client_key
    credentials_type=client_key
    
    # 讀取Client Key的解密密碼:支援從環境變數或者檔案讀取,只需設定一種
    client_key_password_from_env_variable=<your client key private key password environment variable name>
    client_key_password_from_file_path=<your client key private key password file path>
    
    # Client Key的私密金鑰檔案路徑
    client_key_private_key_path=<your client key private key file path>
    
    # 關聯的KMS服務地區
    cache_client_region_id=[{"regionId":"<regionId>"}]
  3. 使用設定檔傳遞憑證資訊。

    import com.aliyun.oss.ClientBuilderConfiguration;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.common.auth.Credentials;
    import com.aliyun.oss.common.auth.CredentialsProvider;
    import com.aliyun.oss.common.auth.DefaultCredentials;
    import com.aliyun.oss.common.comm.SignVersion;
    import com.aliyuncs.kms.secretsmanager.client.SecretCacheClient;
    import com.aliyuncs.kms.secretsmanager.client.SecretCacheClientBuilder;
    import com.aliyuncs.kms.secretsmanager.client.exception.CacheSecretException;
    import com.aliyuncs.kms.secretsmanager.client.model.SecretInfo;
    import org.codehaus.jettison.json.JSONException;
    import org.codehaus.jettison.json.JSONObject;
    
    public class ClientKeyDemoTest {
        public static void main(String[] args) throws CacheSecretException {
            final SecretCacheClient client = SecretCacheClientBuilder.newClient();
            CredentialsProvider credentialsProvider = new CredentialsProvider() {
                @Override
                public void setCredentials(Credentials credentials) {
                }
    
                @Override
                public Credentials getCredentials() {
                    try {
                        SecretInfo secretInfo = client.getSecretInfo("<secretName>");
                        JSONObject jsonObject = new JSONObject(secretInfo.getSecretValue());
    
                        String accessKeyId = jsonObject.getString("AccessKeyId");
                        String accessKeySecret = jsonObject.getString("AccessKeySecret");
    
                        return new DefaultCredentials(accessKeyId, accessKeySecret);
                    } catch (CacheSecretException | JSONException e) {
                        return null;
                    }
                }
            };
            // 使用credentialsProvider初始化用戶端
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
    
            ossClient.shutdown();
        }
    }
    

使用自訂訪問憑證

當以上憑證配置方式都不滿足要求時,可以通過實現Credential Providers介面的方式自訂憑證提供方式。需要注意,如果底層實現基於STS Token,需要提供憑證的更新支援。

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.Credentials;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentials;
import com.aliyun.oss.common.comm.SignVersion;

public class CustomCredentialProviderDemoTest {

    public static void main(String[] args) {

        CredentialsProvider credentialsProvider = new CredentialsProvider(){

            // 初始設定變數
            String accessKeyId = null;
            // 初始設定變數
            String accessKeySecrect = null;
            // 初始設定變數
            // String token = null;

            @Override
            public void setCredentials(Credentials credentials) {
            }

            @Override
            public Credentials getCredentials() {
                //TODO
                //自訂訪問憑證的擷取方法

                // 返回長期憑證 access_key_id, access_key_secrect  
                return new DefaultCredentials(accessKeyId, accessKeySecrect);

                // 返回 臨時憑證 access_key_id, access_key_secrect, token
                // 對於臨時憑證,需要根據到期時間,重新整理憑證。
                // return new DefaultCredentials(accessKeyId, accessKeySecrect, token);
            }
        };
        // 使用credentialsProvider初始化用戶端
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 顯式聲明使用 V4 簽名演算法
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
        // 建立OSSClient執行個體。
        // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
        OSS ossClient = OSSClientBuilder.create()
                .endpoint("endpoint")
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region("region")
                .build();

        ossClient.shutdown();
    }
}

使用預設憑據鏈

初始化憑據用戶端時不傳入任何參數,Credentials工具會使用預設憑據鏈方式初始化用戶端。預設憑據的讀取邏輯請參見預設憑據鏈

  1. 添加credentials依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun/credentials-java -->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>credentials-java</artifactId>
        <version>LATEST</version>
    </dependency>
  2. 配置Credentials作為訪問憑證。

    import com.aliyun.credentials.models.CredentialModel;
    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.Credentials;
    import com.aliyun.oss.common.auth.CredentialsProvider;
    import com.aliyun.oss.common.auth.DefaultCredentials;
    import com.aliyun.oss.common.comm.SignVersion;
    
    public class Demo {
        public static void main(String[] args) {
            com.aliyun.credentials.Client credentialsClient = new com.aliyun.credentials.Client();
            CredentialsProvider credentialsProvider = new CredentialsProvider(){
                @Override
                public void setCredentials(Credentials credentials) {
                }
    
                @Override
                public Credentials getCredentials() {
                    CredentialModel credential = credentialsClient.getCredential();
                    return  new DefaultCredentials(credential.getAccessKeyId(), credential.getAccessKeySecret(), credential.getSecurityToken());
                }
            };
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            // 顯式聲明使用 V4 簽名演算法
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);  
            // 使用credentialsProvider初始化用戶端
            // 建立OSSClient執行個體。
            // 當OSSClient執行個體不再使用時,調用shutdown方法以釋放資源。
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint("endpoint")
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region("region")
                    .build();
            ossClient.shutdown();
        }
    }

常見問題

包衝突

  • 錯誤原因

    使用OSS Java SDK V1時,如出現以下類似錯誤,表明工程中存在包衝突問題。

    Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/ssl/TrustStrategy
        at com.aliyun.oss.OSSClient.<init>(OSSClient.java:268)
        at com.aliyun.oss.OSSClient.<init>(OSSClient.java:193)
        at com.aliyun.oss.demo.HelloOSS.main(HelloOSS.java:77)
    Caused by: java.lang.ClassNotFoundException: org.apache.http.ssl.TrustStrategy
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        ... 3 more
                        

    Exception in thread "main" java.lang.NoSuchFieldError: INSTANCE
     at org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<init>(DefaultHttpRequestWriterFactory.java:52)
     at org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<init>(DefaultHttpRequestWriterFactory.java:56)
     at org.apache.http.impl.io.DefaultHttpRequestWriterFactory.<clinit>(DefaultHttpRequestWriterFactory.java:46)
     at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:82)
     at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:95)
     at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<init>(ManagedHttpClientConnectionFactory.java:104)
     at org.apache.http.impl.conn.ManagedHttpClientConnectionFactory.<clinit>(ManagedHttpClientConnectionFactory.java:62)
     at org.apache.http.impl.conn.PoolingHttpClientConnectionManager$InternalConnectionFactory.<init>(PoolingHttpClientConnectionManager.java:572)
     at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:174)
     at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:158)
     at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:149)
     at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:125)
     at com.aliyun.oss.common.comm.DefaultServiceClient.createHttpClientConnectionManager(DefaultServiceClient.java:237)
     at com.aliyun.oss.common.comm.DefaultServiceClient.<init>(DefaultServiceClient.java:78)
     at com.aliyun.oss.OSSClient.<init>(OSSClient.java:268)
     at com.aliyun.oss.OSSClient.<init>(OSSClient.java:193)
     at OSSManagerImpl.upload(OSSManagerImpl.java:42)
     at OSSManagerImpl.main(OSSManagerImpl.java:63)
                        

    錯誤原因是OSS Java SDK V1使用Apache HttpClient 4.4.1版本,而工程使用了與此版本衝突的Apache HttpClient或commons-httpclient jar包。在工程目錄下執行mvn dependency:tree命令可查看工程使用的jar包及版本。如下圖所示,工程使用了與標準版本衝突的Apache HttpClient 4.3版本:

  • 解決方案

    包衝突有以下兩種解決方案:

    • 使用統一版本:如果工程使用與Apache HttpClient 4.4.1衝突的版本,統一使用4.4.1版本,並在pom.xml中刪除其它版本的Apache HttpClient依賴。如果工程使用了commons-httpclient,同樣可能存在衝突,需刪除commons-httpclient依賴。

    • 解決依賴衝突:如果工程依賴多個第三方包,而第三方包又依賴不同版本的Apache HttpClient,工程將出現依賴衝突,使用exclusion標籤解除衝突。詳細資料請參見Maven Guides

    OSS Java SDK V1依賴以下版本的包,衝突解決辦法與HttpClient類似:

缺少包

  • 錯誤原因

    使用OSS Java SDK V1時,如出現以下類似錯誤,表明工程中缺少編譯或運行OSS Java SDK V1所必需的包。

    Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/auth/Credentials
            at com.aliyun.oss.OSSClient.<init>(OSSClient.java:268)
            at com.aliyun.oss.OSSClient.<init>(OSSClient.java:193)
            at com.aliyun.oss.demo.HelloOSS.main(HelloOSS.java:76)
    Caused by: java.lang.ClassNotFoundException: org.apache.http.auth.Credentials
            at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
            at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
            at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
            ... 3 more
                        

    Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/protocol/HttpContext
            at com.aliyun.oss.OSSClient.<init>(OSSClient.java:268)
            at com.aliyun.oss.OSSClient.<init>(OSSClient.java:193)
            at com.aliyun.oss.demo.HelloOSS.main(HelloOSS.java:76)
    Caused by: java.lang.ClassNotFoundException: org.apache.http.protocol.HttpContext
            at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
            at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
            at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
            ... 3 more
                        

    Exception in thread "main" java.lang.NoClassDefFoundError: org/jdom/input/SAXBuilder
            at com.aliyun.oss.internal.ResponseParsers.getXmlRootElement(ResponseParsers.java:645)
            at … … 
            at com.aliyun.oss.OSSClient.doesBucketExist(OSSClient.java:471)
            at com.aliyun.oss.OSSClient.doesBucketExist(OSSClient.java:465)
            at com.aliyun.oss.demo.HelloOSS.main(HelloOSS.java:82)
    Caused by: java.lang.ClassNotFoundException: org.jdom.input.SAXBuilder
            at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
            at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
            at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
            ... 11 more
                        

    OSS Java SDK V1依賴以下包:

    • aliyun-sdk-oss-2.2.1.jar

    • hamcrest-core-1.1.jar

    • jdom-1.1.jar

    • commons-codec-1.9.jar

    • httpclient-4.4.1.jar

    • commons-logging-1.2.jar

    • httpcore-4.4.1.jar

    • log4j-1.2.15.jar

    其中log4j-1.2.15.jar為可選依賴,僅在需要日誌功能時加入,其它包均為必需依賴。

  • 解決方案

    在工程中加入OSS Java SDK V1依賴的包,具體方法如下:

    • Eclipse專案:請參見安裝 SDK

    • Ant專案:將OSS Java SDK V1依賴的包放入工程的lib目錄中。

    • 直接編譯:使用-classpath-cp命令指定OSS Java SDK V1依賴的包路徑,或將依賴包放入classpath路徑下。

連線逾時

  • 錯誤原因

    運行OSS Java SDK V1程式時出現以下類似錯誤,可能原因為Endpoint配置錯誤或網路連接異常。

    com.aliyun.oss.ClientException: SocketException
        at com.aliyun.oss.common.utils.ExceptionFactory.createNetworkException(ExceptionFactory.java:71)
        at com.aliyun.oss.common.comm.DefaultServiceClient.sendRequestCore(DefaultServiceClient.java:116)
        at com.aliyun.oss.common.comm.ServiceClient.sendRequestImpl(ServiceClient.java:121)
        at com.aliyun.oss.common.comm.ServiceClient.sendRequest(ServiceClient.java:67)
        at com.aliyun.oss.internal.OSSOperation.send(OSSOperation.java:92)
        at com.aliyun.oss.internal.OSSOperation.doOperation(OSSOperation.java:140)
        at com.aliyun.oss.internal.OSSOperation.doOperation(OSSOperation.java:111)
        at com.aliyun.oss.internal.OSSBucketOperation.getBucketInfo(OSSBucketOperation.java:1152)
        at com.aliyun.oss.OSSClient.getBucketInfo(OSSClient.java:1220)
        at com.aliyun.oss.OSSClient.getBucketInfo(OSSClient.java:1214)
        at com.aliyun.oss.demo.HelloOSS.main(HelloOSS.java:94)
    Caused by: org.apache.http.conn.HttpHostConnectException: Connect to oss-test.oss-cn-hangzhou-internal.aliyuncs.com:80 [oss-test.oss-cn-hangzhou-internal.aliyuncs.com/10.84.135.99] failed: Connection timed out: connect
        at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:151)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
        at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
        at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
        at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
        at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
        at com.aliyun.oss.common.comm.DefaultServiceClient.sendRequestCore(DefaultServiceClient.java:113)
        ... 9 more
                        
  • 解決方案

    可以使用ossutil工具快速定位錯誤原因並解決問題。

報錯SignatureDoesNotMatch

  • 錯誤原因1:AccessKey資訊不一致

    AccessKey ID和AccessKey Secret不一致。擷取AccessKey ID和AccessKey Secret的操作步驟請參見建立AccessKey

  • 錯誤原因2:簽名URL使用不正確

    簽名URL使用錯誤樣本如下:

    GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, object);
    request.setExpiration( new Date(new Date().getTime() + 3600 * 1000));
    request.addUserMetadata("author");
    URL url = ossClient.generatePresignedUrl(request);
    
    Map<String, String> header = new HashMap<String, String>();
    header.put("author");
    ossClient.putObject(url, new ByteArrayInputStream("Hello OSS".getBytes()), -1, header);

    未指定Method參數時,預設使用GET方法,但以上為PutObject請求,應指定Method參數並設定為PUT方法。

    通過PutObject發送請求時,請求Header中的自訂中繼資料必須以x-oss-meta-為首碼。以上樣本中的自訂中繼資料應改為x-oss-meta-author

    解決方案

    指定Method參數,並修改Header首碼:

    request.addUserMetadata("author");
    request.setMethod(HttpMethod.PUT);
    URL url = ossClient.generatePresignedUrl(request);
    
    Map<String, String> header = new HashMap<String, String>();
    header.put("x-oss-meta-" + "author");
    ossClient.putObject(url, new ByteArrayInputStream("Hello OSS".getBytes()), -1, header);
  • 錯誤原因3:HttpClient版本相容性問題

    • 使用低於3.7.0版本的OSS SDK,同時專案中引入了4.5.9及以上版本的HttpClient。

    • 上傳的檔案名稱中包含+字元,4.5.9版本的HttpClient不會對+進行URLEncode編碼,導致用戶端與服務端計算的簽名不一致。

    1

    解決方案

    • OSS SDK升級至3.11.1及以上版本,以相容4.5.9版本的HttpClient。

    • 移除多餘的HttpClient依賴。引入OSS SDK時會自動引入HttpClient依賴,如果第三方庫另外引入了HttpClient,請參見包衝突解決方案。

  • 錯誤原因4:HttpClient字元集相容性問題

    HttpClient 4.5.10版本不支援Header中包含ISO/9959-1標準以外的字元,但在專案中引入了4.5.10以上的httpclient,並在請求Header中包含了ISO/9959-1標準以外的字元,例如x-oss-meta-開頭的自訂中繼資料中傳入了中文字元。3

    解決方案

    • 參見包衝突解決方案,移除衝突的HttpClient版本。

    • 在請求Header中僅傳入符合ISO/9959-1標準的字元。

報異常“Failed to parse the response result”

FAQ3

  • 錯誤原因

    用戶端某些特殊的軟體攔截了HTTP請求,或者公網路由劫持了HTTP請求。

    在Java 9及以上的版本中使用OSS Java SDK V1,且未在pom.xml檔案中添加JAXB相關依賴。

  • 解決方案

    切換為HTTPS請求。

    添加JAXB相關依賴。操作步驟,請參見安裝 SDK

org.apache.http.NoHttpResponseException: The target server failed to respond

  • 錯誤原因

    運行OSS Java SDK V1程式時,出現以下類似錯誤:

    使用到期串連會導致上述錯誤,該錯誤僅在Java SDK 2.1.2之前的版本出現。

  • 解決方案

    升級OSS Java SDK V1至2.1.2及以上版本。

JVM中存在大量org.apache.http.impl.conn.PoolingHttpClientConnectionManager執行個體

  • 錯誤原因

    ossClient未正確關閉。

  • 解決方案

    主動關閉已執行完畢的ossClient或使用單例模式。

調用OSS Java SDK V1不響應

  • 錯誤原因

    調用OSS Java SDK V1無響應。通過jstack -l pid命令查看堆棧,問題出現在以下位置:

    "main" prio=6 tid=0x000000000291e000 nid=0xc40 waiting on condition [0x0000000002dae000]
    java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000007d85697f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
        at org.apache.http.pool.PoolEntryFuture.await(PoolEntryFuture.java:138)
        at org.apache.http.pool.AbstractConnPool.getPoolEntryBlocking(AbstractConnPool.java:306)
        at org.apache.http.pool.AbstractConnPool.access$000(AbstractConnPool.java:64)
        at org.apache.http.pool.AbstractConnPool$2.getPoolEntry(AbstractConnPool.java:192)
        at org.apache.http.pool.AbstractConnPool$2.getPoolEntry(AbstractConnPool.java:185)
        at org.apache.http.pool.PoolEntryFuture.get(PoolEntryFuture.java:107)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.leaseConnection(PoolingHttpClientConnectionManager.java:276)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager$1.get(PoolingHttpClientConnectionManager.java:263)
        at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:190)
        at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
        at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
        at com.aliyun.oss.common.comm.DefaultServiceClient.sendRequestCore(DefaultServiceClient.java:113)
        at com.aliyun.oss.common.comm.ServiceClient.sendRequestImpl(ServiceClient.java:123)
        at com.aliyun.oss.common.comm.ServiceClient.sendRequest(ServiceClient.java:68)
        at com.aliyun.oss.internal.OSSOperation.send(OSSOperation.java:94)
        at com.aliyun.oss.internal.OSSOperation.doOperation(OSSOperation.java:146)
        at com.aliyun.oss.internal.OSSOperation.doOperation(OSSOperation.java:113)
        at com.aliyun.oss.internal.OSSObjectOperation.getObject(OSSObjectOperation.java:229)
        at com.aliyun.oss.OSSClient.getObject(OSSClient.java:629)
        at com.aliyun.oss.OSSClient.getObject(OSSClient.java:617)
        at samples.HelloOSS.main(HelloOSS.java:49)
                        

    原因為串連池中串連泄漏,可能是使用ossObject後未正確關閉。

  • 解決方案

    檢查程式,確保沒有串連泄漏。正確關閉方法如下:

    // 讀取檔案
    OSSObject ossObject = ossClient.getObject(bucketName, objectName);
    // OSS操作
    // 關閉ossObject
    ossObject.close();                

    問題排查的具體步驟請參見OSS Java SDK V1不響應問題排查

串連關閉

  • 錯誤原因

    使用ossClient.getObject時,如出現以下類似錯誤:

    Exception in thread "main" org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 11990526; received: 202880)
        at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:180)
        at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:200)
        at org.apache.http.impl.io.ContentLengthInputStream.close(ContentLengthInputStream.java:103)
        at org.apache.http.impl.execchain.ResponseEntityProxy.streamClosed(ResponseEntityProxy.java:128)
        at org.apache.http.conn.EofSensorInputStream.checkClose(EofSensorInputStream.java:228)
        at org.apache.http.conn.EofSensorInputStream.close(EofSensorInputStream.java:174)
        at java.io.FilterInputStream.close(FilterInputStream.java:181)
        at java.io.FilterInputStream.close(FilterInputStream.java:181)
        at com.aliyun.oss.event.ProgressInputStream.close(ProgressInputStream.java:147)
        at java.io.FilterInputStream.close(FilterInputStream.java:181)
        at samples.HelloOSS.main(HelloOSS.java:39)
                        

    原因為兩次讀取資料間隔時間超過1分鐘。OSS會關閉超過1分鐘沒有發送或接收資料的串連。

  • 解決方案

    如果每次僅讀取部分資料,且處理資料的時間不固定,建議使用指定範圍讀取,避免資料讀取時串連關閉。範圍下載完成後,串連會自動關閉。詳細資料請參見範圍下載(Java SDK)

記憶體泄露

  • 錯誤原因

    調用OSS Java SDK V1的程式運行一段時間(根據業務量,幾小時到幾天不等)後出現記憶體泄露。推薦使用Eclipse Memory Analyzer (MAT)分析記憶體使用量情況。詳細資料請參見使用MAT進行堆轉儲檔案分析

    如果分析結果類似下圖所示(PoolingHttpClientConnectionManager佔96%的記憶體),原因為程式中可能多次執行new OSSClient,但未調用ossClient.shutdown,造成記憶體流失。

  • 解決方案

    new OSSClient操作完成後,通過shutdown方法關閉,保證new OSSClientossClient.shutdown成對使用。

調用ossClient.shutdown報異常InterruptedException

  • 錯誤原因

    OSS Java SDK V1 2.3.0之前的版本在調用ossClient.shutdown時報以下異常:

    java.lang.InterruptedException: sleep interrupted
            at java.lang.Thread.sleep(Native Method)
            at com.aliyun.oss.common.comm.IdleConnectionReaper.run(IdleConnectionReaper:76)      

    原因為ossClient後台線程IdleConnectionReaper會定時關閉閑置串連。IdleConnectionReaper在Sleep時調用ossClient.shutdown,就會出現上述異常。

  • 解決方案

    使用以下代碼忽略該異常:

    try {
        ossClient.shutdown();
    } catch(Exception e) {
    }                  

請求出現異常“SDK.ServerUnreachable : Speicified endpoint or uri is not valid”

  • 錯誤原因

    • 使用者端STS並發請求過高。

    • 網路到Server端逾時。

    • 所使用的STS SDK以及SDK core版本過舊。

  • 解決方案

    • 使用者端STS並發請求過高,而使用者端的ECS或本地PC效能不足以承載當時的並發量,降低OSS並發數。

    • 使用者網路到Server端出現逾時現象,可通過抓包驗證。

    • 建議將STS SDK及SDK core升級至最新版本。

NoSuchKey

  • 錯誤原因

    源檔案不存在。

  • 解決方案

    參見404錯誤排查方法。

SocketException

  • 錯誤原因

    可能是socket在初始化階段失敗,導致請求未到達OSS。

  • 解決方案

    建議從以下幾個方面進行排查:

    • 出現問題時是否有網路抖動。

    • 主機的socket串連數是否佔滿。

    • 確認出現問題時串連數是否超過SDK中設定的maxconnection,如果串連數超過maxconnection設定,也會出現socket異常。

    如果以上都沒有問題,建議部署tcpdump或Wireshark抓包,問題複現後分析資料包。

使用OSS PostObject的callback沒有觸發回調

使用OSS PostObject的callback沒有觸發回調,但通過PutObject使用同樣的callback觸發了回調。一般情況下,如果JSON格式有誤或回調失敗,都會返回相應的訊息,需要分別測試Put和Post回調效果:

  • 錯誤原因

    發送請求時callback參數在file下面。image

  • 解決方案

    調整callback參數與file的位置。

    此時測試結果顯示商務服務器成功抓取請求。

Connection pool shut down

Caused by: java.lang.IllegalStateException: Connection pool shut down
  at org.apache.http.util.Asserts.check(Asserts.java:34)
  at org.apache.http.pool.AbstractConnPool.lease(AbstractConnPool.java:184)
  at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.requestConnection(PoolingHttpClientConnectionManager.java:251)
  at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:175)
  at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
  at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
  at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
  at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
  at com.aliyun.oss.common.comm.DefaultServiceClient.sendRequestCore(DefaultServiceClient.java:124)
  at com.aliyun.oss.common.comm.ServiceClient.sendRequestImpl(ServiceClient.java:133)
  ... 8 more
  • 錯誤原因

    調用ossClient.shutdown()介面後,仍繼續通過ossClient發送請求。

  • 解決方案

    檢查調用邏輯,確保調用ossClient.shutdown()介面後,不再通過ossClient發送請求。

使用Java SDK的generatePresignedUrl產生的請求報錯Request has expired

  • 錯誤原因

    int類型溢出,導致2038年時間戳記問題。

    超出URL設定的到期時間後發起上傳請求。

  • 解決方案

    如果是int類型溢出,建議Java SDK中到期時間長度不要超過2038年。

    如果因超出URL設定的到期時間後發起上傳請求,建議設定合理的到期時間,確保到期時間大於發起請求的時間。

報錯Invalid Response或Implementation of JAXB-API has not been found on module path or classpath

  • 錯誤原因

    使用Java 9以上版本,並且未添加JAXB依賴。

  • 解決方案

    關於如何添加JAXB依賴的更多資訊,請參見安裝 SDK

OSS Java SDK V1 中的 OSSClient 是安全執行緒的嗎?

  1. OSSClient 具備安全執行緒特性,允許多線程訪問同一執行個體。結合業務需求,可以複用同一個OSSClient 執行個體,也可以建立多個 OSSClient 執行個體分別使用。

  2. OSSClient 執行個體內部維護串連池。當 OSSClient 執行個體不再使用時,調用 shutdown 方法將其關閉,避免建立過多的 OSSClient 執行個體導致資源耗盡。

報錯AccessDenied Hierarchical namespace is disabled

  • 錯誤原因

    調用CreateDirectory、Rename或DeleteDirectory介面前未開啟階層命名空間。

  • 解決方案

    建立Bucket時開啟階層命名空間。具體步驟請參見建立儲存空間

用戶端網路正常,但是通過HTTP訪問時報錯Connection reset,如何處理?

部分地區的電訊廠商可能會對OSS的網域名稱進行劫持,建議通過Endpoint的方式配置為HTTPS協議。詳細資料請參見配置用戶端

Java 17 Cannot invoke "java.lang.reflect.Method.invoke(Object, Object[])" because "com.sun.xml.bind.v2.runtime.reflect.opt.Injector.defineClass" is null

  • 問題原因

    JAXB在Java 9中被標記為棄用並在Java 11中被刪除。

  • 解決方案

    添加以下依賴:

        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.messaging.saaj</groupId>
            <artifactId>saaj-impl</artifactId>
            <version>1.5.1</version>
        </dependency>

Java SDK如何配置內部日誌列印?

Java SDK使用Apache Commons Logging(JCL)日誌架構進行日誌列印。JCL可以選擇多種日誌實現架構(具體參見:JCL-Configuration),比較常見的是JCL over log4j或JCL over SLF4j兩種,相關實現方式如下:

  • JCL over log4j:需要引入log4j的依賴(log4j 2.x有多種實現架構可選,預設為log4j-api+log4j-core),並按log4j的配置方式進行配置,具體流程參考APACHE LOG4J-API Separation

  • JCL over slf4j:需要引入jcl-over-slf4j 和slf4j的依賴(slf4j也有多種實現架構可選,比如slf4j-api+logback-classic),並按照slf4j的配置方式進行配置,具體流程參考SJF4J-Bridging legacy APIs

Apache Log4j定義了不同層級的日誌,包括OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE和ALL。

通過配置log4j屬性選擇開啟或關閉SDK日誌:

FAQ1