全部產品
Search
文件中心

Object Storage Service:Java授權訪問

更新時間:Oct 25, 2024

本文介紹如何使用STS以及簽名URL臨時授權訪問OSS資源。

注意事項

  • 由於STS臨時帳號以及簽名URL均需設定有效時間長度,當您使用STS臨時帳號產生簽名URL執行相關操作(例如上傳、下載檔案)時,以最小的有效時間長度為準。例如您的STS臨時帳號的有效時間長度設定為1200秒、簽名URL設定為3600秒時,當有效時間長度超過1200秒後,您無法使用此STS臨時帳號產生的簽名URL上傳檔案。

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

  • 本文以華東1(杭州)的外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用格式為https://oss-cn-hangzhou-internal.aliyuncs.com的內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見OSS訪問網域名稱、資料中心、開放連接埠

  • 本文以使用OSS外網Endpoint建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見建立OSSClient

使用STS進行臨時授權

OSS可以通過阿里雲STS(Security Token Service)進行臨時授權訪問。阿里雲STS是為雲端運算使用者提供臨時存取權杖的Web服務。通過STS,您可以為第三方應用或子使用者(即使用者身份由您自己管理的使用者)頒發一個自訂時效和許可權的訪問憑證。關於STS的更多資訊,請參見STS介紹

STS的優勢如下:

  • 您無需透露您的長期密鑰(AccessKey)給第三方應用,只需產生一個存取權杖並將令牌交給第三方應用。您可以自訂這個令牌的存取權限及有效期間限。

  • 您無需關心許可權撤銷問題,存取權杖到期後自動失效。

通過STS臨時授權訪問OSS的步驟如下:

  1. 擷取臨時訪問憑證

    臨時訪問憑證包括臨時存取金鑰(AccessKey ID和AccessKey Secret)和安全性權杖(SecurityToken)。臨時訪問憑證有效時間單位為秒,最小值為900,最大值以當前角色設定的最大會話時間為準。更多資訊,請參見設定RAM角色最大會話時間

    您可以通過以下兩種方式擷取臨時訪問憑證。

    • 方式一

      通過調用STS服務的AssumeRole介面擷取臨時訪問憑證。

    • 方式二

      通過各語言STS SDK擷取臨時訪問憑證。

  2. 使用STS臨時授權上傳和下載檔案。

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.model.GetObjectRequest;
    import com.aliyun.oss.model.PutObjectRequest;
    import java.io.File;
    
    public class Demo {
        public static void main(String[] args) throws Throwable {
            // 以華東1(杭州)的外網Endpoint為例,其它Region請按實際情況填寫。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 從環境變數中擷取從STS服務要求返回的臨時訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET以及OSS_SESSION_TOKEN。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // 填寫Bucket名稱,例如examplebucket。
            String bucketName = "examplebucket";
            // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
            String objectName = "exampleobject.txt";
            // 填寫本地檔案完整路徑。
            String pathName = "D:\\localpath\\examplefile.txt";
    
            // 從STS服務擷取臨時訪問憑證後,您可以通過臨時存取金鑰和安全性權杖產生OSSClient。
            // 填寫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 {            
                // 上傳檔案,此處以上傳本地檔案為例。            
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new File(pathName));
                ossClient.putObject(putObjectRequest);
    
                // 下載OSS檔案到本地檔案。如果指定的本地檔案存在則覆蓋,不存在則建立。            
                //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();
                }
            }
        }
    }

使用簽名URL進行臨時授權

注意事項

  • 產生簽名URL過程中,SDK利用本機存放區的密鑰資訊,根據特定演算法計算出簽名(signature),然後將其附加到URL上,以確保URL的有效性和安全性。這一系列計算和構造URL的操作都是在用戶端完成,不涉及網路請求到服務端。因此,產生簽名URL時不需要授予調用者特定許可權。但是,為避免第三方使用者無法對簽名URL授權的資源執行相關操作,需要確保調用產生簽名URL介面的身份主體被授予對應的許可權。

    例如,通過簽名URL上傳檔案時,需要授予oss:PutObject許可權。通過簽名URL下載或預覽檔案時,需要授予oss:GetObject許可權。

  • 您可以將產生的簽名URL提供給訪客進行臨時訪問。產生簽名URL時,您可以自訂URL的到期時間來限制訪客的訪問時間長度。

  • 如果需要產生HTTPS協議的簽名URL,請將Endpoint中的通訊協定設定為HTTPS。

  • 通過以下樣本產生的簽名URL中如果包含特殊符號+,可能出現無法正常訪問該簽名URL的現象。如需正常訪問該簽名URL,請將簽名URL中的+替換為%2B

以下是使用簽名URL臨時授權的常見樣本。

產生以GET方法訪問的簽名URL

以下代碼用於產生以GET方法訪問的簽名URL。

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import java.net.URL;
import java.util.Date;
import java.util.Date;

public class Demo {
    public static void main(String[] args) throws Throwable {
        // 以華東1(杭州)的外網Endpoint為例,其它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";
        // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
        String objectName = "exampleobject.txt";
        // 填寫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 {
            // 設定簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。
            Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
            // 產生以GET方法訪問的簽名URL,訪客可以直接通過瀏覽器訪問相關內容。
            URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
            System.out.println(url);
        } 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();
            }
        }
    }
}

產生以其他HTTP方法訪問的簽名URL

如果您要授權其他使用者臨時執行其他動作(例如上傳、刪除檔案等),需要產生對應的簽名URL,例如產生以PUT方法訪問的簽名URL來上傳檔案。

以下代碼用於產生以其他HTTP方法訪問的簽名URL。

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.utils.HttpHeaders;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import java.io.ByteArrayInputStream;
import java.net.URL;
import java.util.*;
import java.util.Date;

import static com.aliyun.oss.internal.OSSHeaders.OSS_USER_METADATA_PREFIX;

public class Demo {
    public static void main(String[] args) throws Throwable {
        // 以華東1(杭州)的外網Endpoint為例,其它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";
        // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
        String objectName = "exampleobject.txt";
        // 填寫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 {
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
            // 設定簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。
            Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
            request.setExpiration(expiration);
            // 設定ContentType。
            request.setContentType("text/plain");
            // 設定自訂中繼資料。
            request.addUserMetadata("author", "aliy");

            // 產生簽名URL。
            URL signedUrl = ossClient.generatePresignedUrl(request);
            System.out.println(signedUrl);

            Map<String, String> requestHeaders = new HashMap<String, String>();
            // 設定ContentType,必須和產生簽名URL時設定的ContentType一致。
            requestHeaders.put(HttpHeaders.CONTENT_TYPE, "text/plain");
            // 設定自訂中繼資料。
            requestHeaders.put(OSS_USER_METADATA_PREFIX + "author", "aliy");

            // 使用簽名URL上傳檔案。
            ossClient.putObject(signedUrl, new ByteArrayInputStream("Hello OSS".getBytes()), -1, requestHeaders, true);
        } 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();
            }
        }
    }
}      

通過傳入HttpMethod.PUT參數,訪客可以使用產生的簽名URL上傳檔案。

產生帶有指定參數的簽名URL

  • 產生帶有指定參數的簽名URL

    以下代碼用於產生帶有指定參數的簽名URL。

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class Demo {
        public static void main(String[] args) throws Throwable {
            // 以華東1(杭州)的外網Endpoint為例,其它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";
            // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
            String objectName = "exampleobject.txt";
            // 填寫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 {
                // 建立請求。
                GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, objectName);
                // 設定HttpMethod為PUT。
                generatePresignedUrlRequest.setMethod(HttpMethod.PUT);
                // 添加使用者自訂中繼資料。
                generatePresignedUrlRequest.addUserMetadata("author", "baymax");
                // 設定ContentType。
                generatePresignedUrlRequest.setContentType("application/txt");
                // 設定簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
                generatePresignedUrlRequest.setExpiration(expiration);
                // 產生簽名URL。
                URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
                System.out.println(url);
            } 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();
                }
            }
        }
    }
  • 產生帶有versionId的簽名URL

    以下代碼用於產生帶有versionId的簽名URL。

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class Demo {
        public static void main(String[] args) throws Throwable {
            // 以華東1(杭州)的外網Endpoint為例,其它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";
            // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
            String objectName = "exampleobject.txt";
            // 填寫Object的versionId。
            String versionId = "CAEQARiBgID8rumR2hYiIGUyOTAyZGY2MzU5MjQ5ZjlhYzQzZjNlYTAyZDE3****";
            // 填寫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 {
                // 建立請求。
                GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, objectName);
                // 設定HttpMethod為GET。
                generatePresignedUrlRequest.setMethod(HttpMethod.GET);
                // 設定簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
                generatePresignedUrlRequest.setExpiration(expiration);
                // Object的versionId。
                Map<String, String> queryParam = new HashMap<String, String>();
                queryParam.put("versionId", versionId);
                generatePresignedUrlRequest.setQueryParameter(queryParam);
                // 產生簽名URL。
                URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
                System.out.println(url);
            } 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();
                }
            }
        }
    }

使用簽名URL臨時授權上傳或下載檔案

  • 使用簽名URL上傳檔案

    以下代碼用於產生上傳的簽名URL,並使用簽名URL臨時授權簡單上傳檔案。

    說明

    您也可以先產生簽名URL後再通過該URL臨時授權簡單上傳檔案。關於如何產生簽名URL,請參見URL簽名

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import com.aliyun.oss.model.StorageClass;
    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class Demo {
        public static void main(String[] args) throws Throwable {
            // 以華東1(杭州)的外網Endpoint為例,其它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";
            // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
            String objectName = "exampleobject.txt";
            // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
            String pathName = "D:\\localpath\\examplefile.txt";
            // 填寫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();
            
            // 佈建要求頭。
            Map<String, String> headers = new HashMap<String, String>();
            /*// 指定Object的儲存類型。
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            // 指定ContentType。
            headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/
    
            // 設定使用者自訂中繼資料。
            Map<String, String> userMetadata = new HashMap<String, String>();
            /*userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");*/
    
            URL signedUrl = null;
            try {
                // 指定產生的簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // 產生簽名URL。
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
                // 設定到期時間。
                request.setExpiration(expiration);
    
                // 將要求標頭加入到request中。
                request.setHeaders(headers);
                // 添加使用者自訂中繼資料。
                request.setUserMetadata(userMetadata);
    
                // 通過HTTP PUT請求產生簽名URL。
                signedUrl = ossClient.generatePresignedUrl(request);
                // 列印簽名URL。
                System.out.println("signed url for putObject: " + signedUrl);
    
            } 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());
            }
    
            // 通過簽名URL臨時授權簡單上傳檔案,以HttpClients為例說明。
            putObjectWithHttp(signedUrl, pathName, headers, userMetadata);
        }
    
        public static void putObjectWithHttp(URL signedUrl, String pathName, Map<String, String> headers, Map<String, String> userMetadata) throws IOException {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
            try {
                HttpPut put = new HttpPut(signedUrl.toString());
                HttpEntity entity = new FileEntity(new File(pathName));
                put.setEntity(entity);
                // 如果產生簽名URL時設定了header參數,例如使用者中繼資料,儲存類型等,則呼叫簽章URL上傳檔案時,也需要將這些參數發送至服務端。如果簽名和發送至服務端的不一致,會報簽名錯誤。
                for(Map.Entry header: headers.entrySet()){
                    put.addHeader(header.getKey().toString(),header.getValue().toString());
                }
                for(Map.Entry meta: userMetadata.entrySet()){
                    // 如果使用userMeta,sdk內部會為userMeta拼接"x-oss-meta-"首碼。當您使用其他方式產生簽名URL進行上傳時,userMeta也需要拼接"x-oss-meta-"首碼。
                    put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
                }
    
                httpClient = HttpClients.createDefault();
    
                response = httpClient.execute(put);
    
                System.out.println("返回上傳狀態代碼:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("使用網路程式庫上傳成功");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                response.close();
                httpClient.close();
            }
        }
    }       
  • 使用簽名URL臨時授權分區上傳

    當您希望使用簽名URL以分區上傳的方式上傳大檔案到OSS時,您需要先初始化分區上傳,然後把每一個分區產生一個對應的上傳簽名URL,並返回給第三方應用。第三方應用可以使用這些簽名URL上傳所有的分區資訊,然後合并分區來達到通過簽名URL實現分區上傳的目的。

    以下代碼用於產生分區上傳的簽名URL,並使用簽名URL臨時授權分區上傳。

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.common.comm.io.BoundedInputStream;
    import com.aliyun.oss.common.utils.BinaryUtil;
    import com.aliyun.oss.common.utils.CRC64;
    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.*;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.entity.BufferedHttpEntity;
    import org.apache.http.entity.InputStreamEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.math.BigInteger;
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    import java.util.zip.CheckedInputStream;
    
    public class SignUrlMultipart {
        public static void main(String[] args) throws Throwable {
            // 以華東1(杭州)的外網Endpoint為例,其它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";
            // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
            String objectName = "exampleobject.txt";
            // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
            String pathName = "D:\\localpath\\examplefile.txt";
            // 指定產生的簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。
            long expireTime = 3600*1000L;
            // 填寫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();
    
            // 建立InitiateMultipartUploadRequest對象。
            InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, objectName);
    
            // 初始化分區。
            InitiateMultipartUploadResult upResult = ossClient.initiateMultipartUpload(initRequest);
            // 返回uploadId。uploadId是分區上傳事件的唯一標識。您可以根據該uploadId發起相關的操作,例如取消分區上傳、查詢分區上傳等。
            String uploadId = upResult.getUploadId();
    
            // partETags是PartETag的集合。PartETag由分區的ETag和分區號組成。
            List<PartETag> partETags =  new ArrayList<PartETag>();
            // 每個分區的大小,用於計算檔案有多少個分區。單位為位元組。
            long partSize = 1 * 100 * 1024L;   //100kb。
    
            // 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
            File sampleFile = new File(pathName);
            long fileLength = sampleFile.length();
            // 如果希望設定為1個分區,可以將分區大小設定為檔案大小。
            // long fileLength = sampleFile.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }
    
            // 設定簽名URL的要求標頭。
            Map<String, String> headers = new HashMap<String, String>();
            /*// 指定Object的儲存類型。
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            // 指定ContentType。
            headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/
    
    
            // 遍曆分區擷取分區簽名,並上傳分區。
            // 您還可以一次返回所有分區的簽名URL,然後依次上傳。此處以返回單個簽名URL,並通過簽名URL上傳單個分區為例。
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
    
                /*// 設定md5校正,只支援對單個分區進行md5校正
                FileInputStream inStream = new FileInputStream(pathName);
                // 跳過已經上傳的分區。
                inStream.skip(startPos);
                BoundedInputStream entity = new BoundedInputStream(inStream, partSize);
                String md5 = BinaryUtil.toBase64String(DigestUtils.md5(entity));
                headers.put("Content-MD5", md5);*/
    
                String signUrl = getSignUrl(ossClient, bucketName, objectName, HttpMethod.PUT, expireTime, i + 1, uploadId, headers);
    
                // 通過簽名URL上傳檔案,以HttpClients為例說明。
                putObjectWithHttp(signUrl, pathName, startPos, curPartSize, headers);
            }
    
            // 假設合并分區時,與上傳分區不在同一個系統。此時,您需要先列舉分區,然後再合并分區。
            // 列舉已上傳的分區。
            ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
            PartListing partListing = ossClient.listParts(listPartsRequest);
    
            // 遍曆分區,並填充partETags。
            for (PartSummary part : partListing.getParts()) {
                PartETag partETag = new PartETag(part.getPartNumber(), part.getETag());
                partETags.add(partETag);
            }
    
            // 合并分區。
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
            // String md5 = BinaryUtil.toBase64String(BinaryUtil.calculateMd5("aaa".getBytes()));
            // 設定禁止覆蓋同名檔案。
            // completeMultipartUploadRequest.addHeader("x-oss-forbid-overwrite", "true");
    
            // 完成分區上傳。
            CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
            System.out.println("合并分區成功,上傳分區完成。");
    
    
            // 校正整體上傳檔案是否完整
            CRC64 crc = new CRC64();
            InputStream inStream = new FileInputStream(pathName);
            byte[] bytes = new byte[1024];
            int cnt;
            while ((cnt = inStream.read(bytes)) != -1) {
                crc.update(bytes, 0, cnt);
            }
    
            if(crc.getValue() == completeMultipartUploadResult.getServerCRC()){
                System.out.println("上傳檔案完整");
            } else {
                System.out.println("上傳檔案不完整,請做異常處理");
            }
        }
    
        public static void putObjectWithHttp(String signedUrl, String pathName, long startPos, long partSize, Map<String, String> headers) throws IOException {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
            try {
                HttpPut put = new HttpPut(signedUrl);
    
                FileInputStream inStream = new FileInputStream(pathName);
                // 跳過已經上傳的分區。
                inStream.skip(startPos);
                InputStreamEntity entity = new InputStreamEntity(inStream, partSize);
                BufferedHttpEntity byteArrayEntity = new BufferedHttpEntity(entity);
                put.setEntity(byteArrayEntity);
    
                // 如果產生簽名URL時設定了header參數,例如使用者中繼資料,儲存類型等,則呼叫簽章URL上傳檔案時,也需要將這些參數發送至服務端。如果簽名和發送至服務端的不一致,會報簽名錯誤。
                for(Map.Entry header: headers.entrySet()){
                    put.addHeader(header.getKey().toString(),header.getValue().toString());
                }
    
                // 加入重試,設定為重試3次。這裡僅為舉例,業務代碼根據需要自行設定重試
                httpClient = HttpClients.custom().setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)).build();
    
                response = httpClient.execute(put);
    
                System.out.println("返回上傳狀態代碼:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("使用網路程式庫上傳成功");
                }
                System.out.println(response.toString());
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                if(response != null){
                    response.close();
                }
                if(httpClient != null){
                    httpClient.close();
                }
            }
        }
    
        public static String getSignUrl(OSS ossClient, String bucketName, String objectName, HttpMethod method, long expireTime, int partNum, String uploadId, Map<String, String> headers){
            // 指定產生的簽名URL到期時間,單位為毫秒。
            Date expiration = new Date(new Date().getTime() + expireTime);
    
            // 產生簽名URL。
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, method);
            // 設定到期時間。
            request.setExpiration(expiration);
    
            // 將要求標頭加入到request中。
            request.setHeaders(headers);
    
            request.addQueryParameter("partNumber", String.valueOf(partNum));
    
            request.addQueryParameter("uploadId", uploadId);
    
    
            // 通過HTTP Method請求產生簽名URL。
            URL signedUrl = ossClient.generatePresignedUrl(request);
            // 列印簽名URL。
            System.out.println("signed url: " + signedUrl);
            return signedUrl.toString();
        }
    }
  • 使用簽名URL臨時授權下載檔案

    以下代碼用於產生下載的簽名URL,並使用簽名URL臨時授權下載檔案。

    import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.*;
    import com.aliyun.oss.internal.OSSHeaders;
    import com.aliyun.oss.model.GeneratePresignedUrlRequest;
    import com.aliyun.oss.model.StorageClass;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    import java.util.Date;
    
    public class Demo {
        public static void main(String[] args) throws Throwable {
            // 以華東1(杭州)的外網Endpoint為例,其它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";
            // 填寫Object完整路徑,例如exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
            String objectName = "exampleobject.txt";
            // 填寫下載到本地檔案的完整路徑。
            String pathName = "D:\\localpath\\examplefile.txt";
            // 填寫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();
          
            // 佈建要求頭。
            Map<String, String> headers = new HashMap<String, String>();
            /*// 指定Object的儲存類型。
            headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
            // 指定ContentType。
            headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/
    
            // 設定使用者自訂中繼資料。
            Map<String, String> userMetadata = new HashMap<String, String>();
            /*userMetadata.put("key1","value1");
            userMetadata.put("key2","value2");*/
    
            URL signedUrl = null;
            try {
                // 指定產生的簽名URL到期時間,單位為毫秒。本樣本以設定到期時間為1小時為例。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
    
                // 產生簽名URL。
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.GET);
                // 設定到期時間。
                request.setExpiration(expiration);
    
                // 將要求標頭加入到request中。
                request.setHeaders(headers);
                // 添加使用者自訂中繼資料。
                request.setUserMetadata(userMetadata);
    
                // 設定查詢參數。
                // Map<String, String> queryParam = new HashMap<String, String>();
                // 指定IP地址或者IP位址區段,對應日誌中sourceIpFromSource的值。
                // queryParam.put("x-oss-ac-source-ip","192.0.2.0");
                // 將子網路遮罩轉換為二進位,然後填寫轉換結果中1的數量。
                // queryParam.put("x-oss-ac-subnet-mask","32");
                // 指定VPC ID。
                // queryParam.put("x-oss-ac-vpc-id","vpc-12345678");
                // 指定是否允許轉寄請求。
                // queryParam.put("x-oss-ac-forward-allow","true");
                // request.setQueryParameter(queryParam);
    
                // 設定單連結限速,單位為bit,例如限速100 KB/s。
                // request.setTrafficLimit(100 * 1024 * 8);
    
                // 通過HTTP GET請求產生簽名URL。
                signedUrl = ossClient.generatePresignedUrl(request);
                // 列印簽名URL。
                System.out.println("signed url for putObject: " + signedUrl);
            } 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());
            }
    
            // 通過簽名URL下載檔案,以HttpClients為例說明。
            getObjectWithHttp(signedUrl, pathName, headers, userMetadata);
        }
    
        public static void getObjectWithHttp(URL signedUrl, String pathName, Map<String, String> headers, Map<String, String> userMetadata) throws IOException {
            CloseableHttpClient httpClient = null;
            CloseableHttpResponse response = null;
            try {
                HttpGet get = new HttpGet(signedUrl.toString());
    
                // 如果產生簽名URL時設定了header參數,例如使用者中繼資料,儲存類型等,則呼叫簽章URL下載檔案時,也需要將這些參數發送至服務端。如果簽名和發送至服務端的不一致,會報簽名錯誤。
                for(Map.Entry header: headers.entrySet()){
                    get.addHeader(header.getKey().toString(),header.getValue().toString());
                }
                for(Map.Entry meta: userMetadata.entrySet()){
                    // 如果使用userMeta,sdk內部會為userMeta拼接"x-oss-meta-"首碼。當您使用其他方式產生簽名URL進行下載時,userMeta也需要拼接"x-oss-meta-"首碼。
                    get.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
                }
    
                httpClient = HttpClients.createDefault();
                response = httpClient.execute(get);
    
                System.out.println("返回下載狀態代碼:"+response.getStatusLine().getStatusCode());
                if(response.getStatusLine().getStatusCode() == 200){
                    System.out.println("使用網路程式庫下載成功");
                }
                System.out.println(response.toString());
    
                // 儲存檔案到磁碟。
                saveFileToLocally(response.getEntity().getContent(), pathName);
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                response.close();
                httpClient.close();
            }
        }
    
        public static void saveFileToLocally(InputStream inputStream, String pathName) throws IOException {
            DataInputStream in = null;
            OutputStream out = null;
            try {
                in = new DataInputStream(inputStream);
                out = new DataOutputStream(new FileOutputStream(pathName));
                int bytes = 0;
                byte[] bufferOut = new byte[1024];
                while ((bytes = in.read(bufferOut)) != -1) {
                    out.write(bufferOut, 0, bytes);
                }
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                in.close();
                out.close();
            }
        }
    }

常見問題

使用臨時簽名進行檔案上傳時,在上傳過程中籤名到期了,上傳中的檔案會失敗嗎?

簡單上傳時不會失敗。

如果是分區上傳,上傳過程中籤名到期了,可能影響其餘分區的上傳。