本文介紹如何使用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外網Endpoint新建OSSClient為例。如果您希望通過自定義域名、STS等方式新建OSSClient,請參見新建OSSClient。
使用STS進行臨時授權
OSS可以通過阿里云STS(Security Token Service)進行臨時授權訪問。阿里云STS是為云計算用戶提供臨時訪問令牌的Web服務。通過STS,您可以為第三方應用或子用戶(即用戶身份由您自己管理的用戶)頒發一個自定義時效和權限的訪問憑證。關于STS的更多信息,請參見STS介紹。
STS的優勢如下:
您無需透露您的長期密鑰(AccessKey)給第三方應用,只需生成一個訪問令牌并將令牌交給第三方應用。您可以自定義這個令牌的訪問權限及有效期限。
您無需關心權限撤銷問題,訪問令牌過期后自動失效。
通過STS臨時授權訪問OSS的步驟如下:
獲取臨時訪問憑證
臨時訪問憑證包括臨時訪問密鑰(AccessKey ID和AccessKey Secret)和安全令牌(SecurityToken)。臨時訪問憑證有效時間單位為秒,最小值為900,最大值以當前角色設定的最大會話時間為準。更多信息,請參見設置RAM角色最大會話時間。
您可以通過以下兩種方式獲取臨時訪問憑證。
方式一
通過調用STS服務的AssumeRole接口獲取臨時訪問憑證。
方式二
通過各語言STS SDK獲取臨時訪問憑證。
使用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。 // 創建OSSClient實例。 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); 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";
// 創建OSSClient實例。
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
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";
// 創建OSSClient實例。
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
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"; // 創建OSSClient實例。 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); 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****"; // 創建OSSClient實例。 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); 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"; // 創建OSSClient實例 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); // 設置請求頭。 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; // 創建OSSClient實例。 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); // 創建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"; // 創建OSSClient實例。 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); // 設置請求頭。 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(); } } }
常見問題
使用臨時簽名進行文件上傳時,在上傳過程中簽名過期了,上傳中的文件會失敗嗎?
簡單上傳時不會失敗。
如果是分片上傳,上傳過程中簽名過期了,可能影響其余分片的上傳。