安裝OSS JAVA SDK
直接在Eclipse中使用JAR包
步驟如下:
- 在官方網站下載 Open Service Java SDK 。
- 解壓文件。
- 將解壓后文件夾中的文件: aliyun-openservice-<versionId>.jar 以及lib文件夾下的所有文件拷貝到你的工程文件夾中。
- 在Eclipse右鍵工程 -> Properties -> Java Build Path -> Add JARs 。
- 選擇你拷貝的所有JAR文件。
經過上面幾步之后,你就可以在工程中使用OSS JAVA SDK了。
在Maven工程中使用SDK¶
在Maven工程中使用JAVA SDK十分簡單,只要在在pom.xml文件中加入依賴就可以了。
在 dependencies 標簽內加入如下內容:
<dependency>
<groupId>com.aliyun.openservices</groupId> <artifactId>aliyun-openservices</artifactId> <version>1.0.10</version> </dependency>
version為版本號,隨着版本更新可能有變動。
快速入門
在這一章里,您將學到如何用OSS Java SDK完成一些基本的操作。
Step 1. 初始化一個OSSClient
OSSClient是與OSS服務交互的客戶端,SDK的OSS操作都是通過OSSClient完成的。
下面代碼新建了一個OSSClient:
import com.aliyun.openservices.oss.OSSClient; public class Sample { public static void main(String[] args) { String accessKeyId = "<key>"; String accessKeySecret = "<secret>"; // 初始化一個OSSClient OSSClient client = new OSSClient(accessKeyId, accessKeySecret); // 下面是一些調用代碼... ... } }
在上面代碼中,變量 accessKeyId 與 accessKeySecret 是由系統分配給用戶的,稱為ID對,用於標識用戶,為訪問OSS做簽名驗證。
關於OSSClient的詳細介紹,參見 OSSClient 。
Step 2. 新建Bucket
Bucket是OSS上的命名空間,相當於數據的容器,可以存儲若干數據實體(Object)。
你可以按照下面的代碼新建一個Bucket:
public void createBucket(String bucketName) { // 初始化OSSClient OSSClient client = ...; // 新建一個Bucket client.createBucket(bucketName); }
由於Bucket的名字是全局唯一的,所以盡量保證你的 bucketName 不與別人重復。
關於Bucket的命名規范,參見 Bucket命名規范。
Step 3. 上傳Object
Object是OSS中最基本的數據單元,你可以把它簡單地理解為文件,用下面代碼可以實現一個Object的上傳:
public void putObject(String bucketName, String key, String filePath) throws FileNotFoundException { // 初始化OSSClient OSSClient client = ...; // 獲取指定文件的輸入流 File file = new File(filePath); InputStream content = new FileInputStream(file); // 創建上傳Object的Metadata ObjectMetadata meta = new ObjectMetadata(); // 必須設置ContentLength meta.setContentLength(file.length()); // 上傳Object. PutObjectResult result = client.putObject(bucketName, key, content, meta); // 打印ETag System.out.println(result.getETag()); }
Object通過InputStream的形式上傳到OSS中。在上面的例子里我們可以看出,每上傳一個Object,都需要指定和Object關聯的ObjectMetadata。ObjectMetaData是用戶對該object的描述,由一系列name-value對組成;其中ContentLength是必須設置的,以便SDK可以正確識別上傳Object的大小。
Put Object請求處理成功后,OSS會將收到文件的MD5值放在返回結果的ETag中。用戶可以根據ETag檢驗上傳的文件與本地的是否一致。
關於Object的命名規范,參見 Object命名規范 。
關於上傳Object更詳細的信息,參見 上傳Object 。
Step 4. 列出所有Object
當你完成一系列上傳后,可能會需要查看在某個Bucket中有哪些Object,可以通過下面的程序實現:
public void listObjects(String bucketName) { // 初始化OSSClient OSSClient client = ...; // 獲取指定bucket下的所有Object信息 ObjectListing listing = client.listObjects(bucketName); // 遍歷所有Object for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) { System.out.println(objectSummary.getKey()); } }
listObjects方法會返回ObjectListing對象,ObjectListing對象包含了此次listObject請求的返回結果。其中我們可以通過ObjetListing中的getObjectSummaries方法獲取所有Object的描述信息(List<OSSObjectSummary>)。
Step 5. 獲取指定Object
你可以參考下面的代碼簡單地實現一個Object的獲取:
public void getObject(String bucketName, String key) throws IOException { // 初始化OSSClient OSSClient client = ...; // 獲取Object,返回結果為OSSObject對象 OSSObject object = client.getObject(bucketName, key); // 獲取Object的輸入流 InputStream objectContent = object.getObjectContent(); // 處理Object ... // 關閉流 objectContent.close(); }
當調用OSSClient的getObject方法時,會返回一個OSSObject的對象,此對象包含了Object的各種信息。通過OSSObject的getObjectContent方法,還可以獲取返回的Object的輸入流,你可以讀取這個輸入流來對Object的內容進行操作;記得在用完之后關閉這個流。
OSSClient
OSSClient是OSS服務的Java客戶端,它為調用者提供了一系列的方法,用於和OSS服務進行交互。
新建OSSClient
新建一個OSSClient很簡單,如下面代碼所示:
String key = "<key>"; String secret = "<secret>"; OSSClient client = new OSSClient(key, secret);
上面的方式使用默認域名作為OSS的服務地址,如果你想自己指定域名,可以傳入endpoint參數來指定。
String key = "<key>"; String secret = "<secret>"; String endpoint = "http://oss.aliyuncs.com"; OSSClient client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
配置OSSClient
如果你想配置OSSClient的一些細節的參數,可以在構造OSSClient的時候傳入ClientConfiguration對象。 ClientConfiguration是OSS服務的配置類,可以為客戶端配置代理,最大連接數等參數。
使用代理
下面一段代碼可以使客戶端使用代理訪問OSS服務:
// 創建ClientConfiguration實例
ClientConfiguration conf = new ClientConfiguration(); // 配置代理為本地8080端口 conf.setProxyHost("127.0.0.1"); conf.setProxyPort(8080); // 創建OSS客戶端 client = new OSSClient(endpoint, accessKeySecret, accessKeySecret, conf);
上面代碼使得客戶端的所有操作都會使用127.0.0.1地址的8080端口做代理執行。
對於有用戶驗證的代理,可以配置用戶名和密碼:
// 創建ClientConfiguration實例
ClientConfiguration conf = new ClientConfiguration(); // 配置代理為本地8080端口 conf.setProxyHost("127.0.0.1"); conf.setProxyPort(8080); //設置用戶名和密碼 conf.setProxyUsername("username"); conf.setProxyPassword("password");
設置網絡參數
我們可以用ClientConfiguration設置一些網絡參數:
ClientConfiguration conf = new ClientConfiguration(); // 設置HTTP最大連接數為10 conf.setMaxConnections(10); // 設置TCP連接超時為5000毫秒 conf.setConnectionTimeout(5000); // 設置最大的重試次數為3 conf.setMaxErrorRetry(3); // 設置Socket傳輸數據超時的時間為2000毫秒 conf.setSocketTimeout(2000);
ClientConfiguration所有參數
通過ClientConfiguration能指定的所有參數如下表所示:
參數 | 說明 |
---|---|
UserAgent | 用戶代理,指HTTP的User-Agent頭。默認為”aliyun-sdk-java” |
ProxyHost | 代理服務器主機地址 |
ProxyPort | 代理服務器端口 |
ProxyUsername | 代理服務器驗證的用戶名 |
ProxyPassword | 代理服務器驗證的密碼 |
ProxyDomain | 訪問NTLM驗證的代理服務器的Windows域名 |
ProxyWorkstation | NTLM代理服務器的Windows工作站名稱 |
MaxConnections | 允許打開的最大HTTP連接數。默認為50 |
SocketTimeout | 通過打開的連接傳輸數據的超時時間(單位:毫秒)。默認為50000毫秒 |
ConnectionTimeout | 建立連接的超時時間(單位:毫秒)。默認為50000毫秒 |
MaxErrorRetry | 可重試的請求失敗后最大的重試次數。默認為3次 |
Bucket
Bucket是OSS上的命名空間,也是計費、權限控制、日志記錄等高級功能的管理實體;Bucket名稱在整個OSS服務中具有全局唯一性,且不能修改;存儲在OSS上的每個Object必須都包含在某個Bucket中。一個應用,例如圖片分享網站,可以對應一個或多個Bucket。一個用戶最多可創建10個Bucket,但每個Bucket中存放的Object的數量和大小總和沒有限制,用戶不需要考慮數據的可擴展性。
命名規范
Bucket的命名有以下規范:
- 只能包括小寫字母,數字,短橫線(-)
- 必須以小寫字母或者數字開頭
- 長度必須在3-63字節之間
新建Bucket
如下代碼可以新建一個Bucket:
String bucketName = "my-bucket-name"; // 初始化OSSClient OSSClient client = ...; // 新建一個Bucket client.createBucket(bucketName);
由於Bucket的名字是全局唯一的,所以盡量保證你的 bucketName 不與別人重復。
列出用戶所有的Bucket
下面代碼可以列出用戶所有的Bucket:
// 獲取用戶的Bucket列表
List<Bucket> buckets = client.listBuckets(); // 遍歷Bucket for (Bucket bucket : buckets) { System.out.println(bucket.getName()); }
判斷Bucket是否存在
有時候,我們的需求只是判斷Bucket是否存在。則下面代碼可以做到:
String bucketName = "your-bucket-name"; // 獲取Bucket的存在信息 boolean exists = client.doesBucketExist(bucketName); // 輸出結果 if (exists) { System.out.println("Bucket exists"); } else { System.out.println("Bucket not exists"); }
刪除Bucket
下面代碼刪除了一個Bucket:
String bucketName = "your-bucket-name"; // 刪除Bucket client.deleteBucket(bucketName)
需要注意的是,如果Bucket不為空(Bucket中有Object),則Bucket無法刪除,必須清空Bucket后才能成功刪除。
Bucket權限控制
Bucket的訪問權限
OSS提供Bucket級別的權限訪問控制,Bucket目前有三種訪問權限:public-read-write,public-read和private。它們的含義如下:
- public-read-write: 任何人(包括匿名訪問)都可以對該bucket中的object進行上傳、下載和刪除操作;所有這些操作產生的費用由該bucket的創建者承擔,請慎用該權限。
- public-read: 只有該bucket的創建者可以對該bucket內的Object進行寫操作(包括上傳和刪除);任何人(包括匿名訪問)可以對該bucket中的object進行讀操作。
- private: 只有該bucket的創建者才可以訪問此Bukcet。其他人禁止對此Bucket做任何操作。
用戶新創建一個新Bucket時,如果不指定Bucket權限,OSS會自動為該Bucket設置private權限。對於一個已經存在的Bucket,只有它的創建者可以通過OSS的所提供的接口修改其訪問權限。
修改Bucket的訪問權限
下面代碼將Bucket的權限設置為了private。
String bucketName = "your-bucket-name"; client.setBucketAcl(bucketName, CannedAccessControlList.Private);
CannedAccessControlList是枚舉類型,包含三個值: Private 、 PublicRead 、 PublicReadWrite ,它們分別對應相關權限。
Object
在OSS中,用戶操作的基本數據單元是Object。單個Object最大允許存儲5TB的數據。Object包含key、meta和data。其中,key是Object的名字;meta是用戶對該object的描述,由一系列name-value對組成;data是Object的數據。
命名規范
Object的命名規范如下:
- 使用UTF-8編碼
- 長度必須在1-1023字節之間
- 不能以“/”或者“\”字符開頭
- 不能含有“\r”或者“\n”的換行符
上傳Object
最簡單的上傳
如下代碼:
public void putObject(String bucketName, String key, String filePath) throws FileNotFoundException { // 初始化OSSClient OSSClient client = ...; // 獲取指定文件的輸入流 File file = new File(filePath); InputStream content = new FileInputStream(file); // 創建上傳Object的Metadata ObjectMetadata meta = new ObjectMetadata(); // 必須設置ContentLength meta.setContentLength(file.length()); // 上傳Object. PutObjectResult result = client.putObject(bucketName, key, content, meta); // 打印ETag System.out.println(result.getETag()); }
Object通過InputStream的形式上傳到OSS中。在上面的例子里我們可以看出,每上傳一個Object,都需要指定和Object關聯的ObjectMetadata。ObjectMetaData是用戶對該object的描述,由一系列name-value對組成;其中ContentLength是必須設置的,以便SDK可以正確識別上傳Object的大小。
Put Object請求處理成功后,OSS會將收到文件的MD5值放在返回結果的ETag中。用戶可以根據ETag檢驗上傳的文件與本地的是否一致。
設定Object的Http Header
OSS Java SDK本質上是調用后台的HTTP接口,因此OSS服務允許用戶自定義Object的Http Header。下面代碼為Object設置了過期時間:
// 初始化OSSClient
OSSClient client = ...; // 初始化上傳輸入流 InputStream content = ...; // 創建上傳Object的Metadata ObjectMetadata meta = new ObjectMetadata(); // 設置ContentLength為1000 meta.setContentLength(1000); // 設置1小時后過期 Date expire = new Date(new Date().getTime() + 3600 * 1000); meta.setExpirationTime(expire); client.putObject(bucketName, key, content, meta);
Java SDK支持的Http Header有四種,分別為:Cache-Control 、 Content-Disposition 、Content-Encoding 、 Expires 。它們的相關介紹見 RFC2616 。
用戶自定義元數據
OSS支持用戶自定義元數據來對Object進行描述。比如:
// 設置自定義元數據name的值為my-data
meta.addUserMetadata("name", "my-data"); // 上傳object client.putObject(bucketName, key, content, meta);
在上面代碼中,用戶自定義了一個名字為”name”,值為”my-data”的元數據。當用戶下載此Object的時候,此元數據也可以一並得到。一個Object可以有多個類似的參數,但所有的user meta總大小不能超過2k。
分塊上傳
OSS允許用戶將一個Object分成多個請求上傳到后台服務器中,關於分塊上傳的內容,我們將在 Object的分塊上傳 這一章中做介紹。
列出Bucket中的Object
列出Object
public void listObjects(String bucketName) { // 初始化OSSClient OSSClient client = ...; // 獲取指定bucket下的所有Object信息 ObjectListing listing = client.listObjects(bucketName); // 遍歷所有Object for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) { System.out.println(objectSummary.getKey()); } }
listObjects方法會返回 ObjectListing 對象,ObjectListing 對象包含了此次listObject請求的返回結果。其中我們可以通過 ObjetListing 中的 getObjectSummaries 方法獲取所有Object的描述信息(List<OSSObjectSummary>)。
Note
默認情況下,如果Bucket中的Object數量大於100,則只會返回100個Object, 且返回結果中 IsTruncated 為 false,並返回 NextMarker 作為下此讀取的起點。若想增大返回Object數目,可以修改 MaxKeys 參數,或者使用 Marker 參數分次讀取。
擴展參數
通常,我們可以通過設置ListObjectsRequest的參數來完成更強大的功能。比如:
// 構造ListObjectsRequest請求
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName); // 設置參數 listObjectsRequest.setDelimiter("/"); listObjectsRequest.setMarker("123"); ... ObjectListing listing = client.listObjects(listObjectsRequest);
上面代碼中我們調用了 listObjects 的一個重載方法,通過傳入 ListObjectsRequest 來完成請求。通過 ListObjectsRequest 中的參數設置我們可以完成很多擴展的功能。下表列出了 ListObjectsRequest 中可以設置的參數名稱和作用:
名稱 | 作用 |
---|---|
Delimiter | 是一個用於對Object名字進行分組的字符。所有名字包含指定的前綴且第一次出現Delimiter字符之間的object作為一組元素: CommonPrefixes。 |
Marker | 設定結果從Marker之后按字母排序的第一個開始返回。 |
MaxKeys | 限定此次返回object的最大數,如果不設定,默認為100,MaxKeys取值不能大於1000。 |
Prefix | 限定返回的object key必須以Prefix作為前綴。注意使用prefix查詢時,返回的key中仍會包含Prefix。 |
文件夾功能模擬
我們可以通過 Delimiter 和 Prefix 參數的配合模擬出文件夾功能。
Delimiter 和 Prefix 的組合效果是這樣的:如果把 Prefix 設為某個文件夾名,就可以羅列以此 Prefix 開頭的文件,即該文件夾下遞歸的所有的文件和子文件夾。如果再把 Delimiter 設置為 “/” 時,返回值就只羅列該文件夾下的文件,該文件夾下的子文件名返回在 CommonPrefixes 部分,子文件夾下遞歸的文件和文件夾不被顯示.
假設Bucket中有4個文件: oss.jpg , fun/test.jpg , fun/movie/001.avi , fun/movie/007.avi ,我們把 “/” 符號作為文件夾的分隔符。
列出Bucket內所有文件
當我們需要獲取Bucket下的所有文件時,可以這樣寫:
// 構造ListObjectsRequest請求
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName); // List Objects ObjectListing listing = client.listObjects(listObjectsRequest); // 遍歷所有Object System.out.println("Objects:"); for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) { System.out.println(objectSummary.getKey()); } // 遍歷所有CommonPrefix System.out.println("CommonPrefixs:"); for (String commonPrefix : listing.getCommonPrefixes()) { System.out.println(commonPrefix); }
輸出:
Objects:
fun/movie/001.avi
fun/movie/007.avi
fun/test.jpg
oss.jpg
CommonPrefixs:
遞歸列出目錄下所有文件
我們可以通過設置 Prefix 參數來獲取某個目錄下所有的文件:
// 構造ListObjectsRequest請求
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName); // 遞歸列出fun目錄下的所有文件 listObjectsRequest.setPrefix("fun/"); ObjectListing listing = client.listObjects(listObjectsRequest); // 遍歷所有Object System.out.println("Objects:"); for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) { System.out.println(objectSummary.getKey()); } // 遍歷所有CommonPrefix System.out.println("\nCommonPrefixs:"); for (String commonPrefix : listing.getCommonPrefixes()) { System.out.println(commonPrefix); }
輸出:
Objects:
fun/movie/001.avi
fun/movie/007.avi
fun/test.jpg
CommonPrefixs:
列出目錄下的文件和子目錄
在 Prefix 和 Delimiter 結合的情況下,可以列出目錄下的文件和子目錄:
// 構造ListObjectsRequest請求
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName); // "/" 為文件夾的分隔符 listObjectsRequest.setDelimiter("/"); // 列出fun目錄下的所有文件和文件夾 listObjectsRequest.setPrefix("fun/"); ObjectListing listing = client.listObjects(listObjectsRequest); // 遍歷所有Object System.out.println("Objects:"); for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) { System.out.println(objectSummary.getKey()); } // 遍歷所有CommonPrefix System.out.println("\nCommonPrefixs:"); for (String commonPrefix : listing.getCommonPrefixes()) { System.out.println(commonPrefix); }
輸出:
Objects:
fun/test.jpg
CommonPrefixs:
fun/movie/
返回的結果中, ObjectSummaries 的列表中給出的是fun目錄下的文件。而 CommonPrefixs 的列表中給出的是fun目錄下的所有子文件夾。可以看出 fun/movie/001.avi , fun/movie/007.avi 兩個文件並沒有被列出來,因為它們屬於 fun 文件夾下的 movie 目錄。
獲取Object
簡單的讀取Object
我們可以通過以下代碼將Object讀取到一個流中:
public void getObject(String bucketName, String key) throws IOException { // 初始化OSSClient OSSClient client = ...; // 獲取Object,返回結果為OSSObject對象 OSSObject object = client.getObject(bucketName, key); // 獲取ObjectMeta ObjectMetadata meta = object.getObjectMetadata(); // 獲取Object的輸入流 InputStream objectContent = object.getObjectContent(); // 處理Object ... // 關閉流 objectContent.close(); }
`OSSObject 包含了Object的各種信息,包含Object所在的Bucket、Object的名稱、Metadata以及一個輸入流。我們可以通過操作輸入流將Object的內容讀取到文件或者內存中。而ObjectMetadata包含了Object上傳時定義的,ETag,Http Header以及自定義的元數據。
通過GetObjectRequest獲取Object
為了實現更多的功能,我們可以通過使用 GetObjectRequest 來獲取Object。
// 初始化OSSClient
OSSClient client = ...; // 新建GetObjectRequest GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key); // 獲取0~100字節范圍內的數據 getObjectRequest.setRange(0, 100); // 獲取Object,返回結果為OSSObject對象 OSSObject object = client.getObject(getObjectRequest);
我們通過 getObjectRequest 的 setRange 方法設置了返回的Object的范圍。我們可以用此功能實現文件的分段下載和斷點續傳。
GetObjectRequest可以設置以下參數:
參數 | 說明 |
---|---|
Range | 指定文件傳輸的范圍。 |
ModifiedSinceConstraint | 如果指定的時間早於實際修改時間,則正常傳送文件。否則拋出304 Not Modified異常。 |
UnmodifiedSinceConstraint | 如果傳入參數中的時間等於或者晚於文件實際修改時間,則正常傳輸文件。否則拋出412 precondition failed異常 |
MatchingETagConstraints | 傳入一組ETag,如果傳入期望的ETag和object的 ETag匹配,則正常傳輸文件。否則拋出412 precondition failed異常 |
NonmatchingEtagConstraints | 傳入一組ETag,如果傳入的ETag值和Object的ETag不匹配,則正常傳輸文件。否則拋出304 Not Modified異常。 |
ResponseHeaderOverrides | 自定義OSS返回請求中的一些Header。 |
修改 ResponseHeaderOverrides , 它提供了一系列的可修改參數,可以自定義OSS的返回Header,如下表所示:
參數 | 說明 |
---|---|
ContentType | OSS返回請求的content-type頭 |
ContentLanguage | OSS返回請求的content-language頭 |
Expires | OSS返回請求的expires頭 |
CacheControl | OSS返回請求的cache-control頭 |
ContentDisposition | OSS返回請求的content-disposition頭 |
ContentEncoding | OSS返回請求的content-encoding頭 |
直接下載Object到文件
我們可以通過下面的代碼直接將Object下載到指定文件:
// 新建GetObjectRequest
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key); // 下載Object到文件 ObjectMetadata objectMetadata = client.getObject(getObjectRequest, new File("/path/to/file"));
當使用上面方法將Object直接下載到文件時,方法返回ObjectMetadata對象。
只獲取ObjectMetadata
通過 getObjectMetadata 方法可以只獲取ObjectMetadata而不獲取Object的實體。如下代碼所示:
ObjectMetadata objectMetadata = client.getObjectMetadata(bucketName, key);
刪除Object
下面代碼刪除了一個Object:
public void deleteObject(String bucketName, String key) { // 初始化OSSClient OSSClient client = ...; // 刪除Object client.deleteObject(bucketName, key); }
拷貝Object
拷貝一個Object
通過 copyObject 方法我們可以拷貝一個Object,如下面代碼:
public void copyObject(String srcBucketName, String srcKey, String destBucketName, String destKey) { // 初始化OSSClient OSSClient client = ...; // 拷貝Object CopyObjectResult result = client.copyObject(srcBucketName, srcKey, destBucketName, destKey); // 打印結果 System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified()); }
copyObject 方法返回一個 CopyObjectResult 對象,對象中包含了新Object的ETag和修改時間。
通過CopyObjectRequest拷貝Object
也可以通過 CopyObjectRequest 實現Object的拷貝:
// 初始化OSSClient
OSSClient client = ...; // 創建CopyObjectRequest對象 CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketName, srcKey, destBucketName, destKey); // 設置新的Metadata ObjectMetadata meta = new ObjectMetadata(); meta.setContentType("text/html"); copyObjectRequest.setNewObjectMetadata(meta); // 復制Object CopyObjectResult result = client.copyObject(copyObjectRequest); System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
CopyObjectRequest 允許用戶修改目的Object的ObjectMeta,同時也提供 ModifiedSinceConstraint , UnmodifiedSinceConstraint , MatchingETagConstraints , NonmatchingEtagConstraints 四個參數的設定, 用法與 GetObjectRequest 的參數相似,參見 GetObjectRequest的可設置參數。
Object的分塊上傳
除了通過putObject接口上傳文件到OSS以外,OSS還提供了另外一種上傳模式 —— Multipart Upload。用戶可以在如下的應用場景內(但不僅限於此),使用Multipart Upload上傳模式,如:
- 需要支持斷點上傳。
- 上傳超過100MB大小的文件。
- 網絡條件較差,和OSS的服務器之間的鏈接經常斷開。
- 需要流式地上傳文件。
- 上傳文件之前,無法確定上傳文件的大小。
下面我們將一步步介紹怎樣實現Multipart Upload。
分步完成Multipart Upload
假設我們有一個文件,本地路徑為 /path/to/file.zip 由於文件比較大,我們將其分塊傳輸到OSS中。
1. 初始化Multipart Upload
我們使用 initiateMultipartUpload 方法來初始化一個分塊上傳事件:
String bucketName = "your-bucket-name"; String key = "your-key"; // 初始化OSSClient OSSClient client = ...; // 開始Multipart Upload InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucketName, key); InitiateMultipartUploadResult initiateMultipartUploadResult = client.initiateMultipartUpload(initiateMultipartUploadRequest); // 打印UploadId System.out.println("UploadId: " + initiateMultipartUploadResult.getUploadId());
我們用 InitiateMultipartUploadRequest 來指定上傳Object的名字和所屬Bucket。在 InitiateMultipartUploadRequest 中,你也可以設置 ObjectMetadata ,但是不必指定其中的 ContentLength (指定了也無效)。
initiateMultipartUpload 的返回結果中含有 UploadId ,它是區分分塊上傳事件的唯一標識,在后面的操作中,我們將用到它。
2. 上傳分塊
接着,我們把文件分塊上傳。
// 設置每塊為 5M
final int partSize = 1024 * 1024 * 5; File partFile = new File("/path/to/file.zip"); // 計算分塊數目 int partCount = (int) (partFile.length() / partSize); if (partFile.length() % partSize != 0){ partCount++; } // 新建一個List保存每個分塊上傳后的ETag和PartNumber List<PartETag> partETags = new ArrayList<PartETag>(); for(int i = 0; i < partCount; i++){ // 獲取文件流 FileInputStream fis = new FileInputStream(partFile); // 跳到每個分塊的開頭 long skipBytes = partSize * i; fis.skip(skipBytes); // 計算每個分塊的大小 long size = partSize < partFile.length() - skipBytes ? partSize : partFile.length() - skipBytes; // 創建UploadPartRequest,上傳分塊 UploadPartRequest uploadPartRequest = new UploadPartRequest(); uploadPartRequest.setBucketName(bucketName); uploadPartRequest.setKey(key); uploadPartRequest.setUploadId(initiateMultipartUploadResult.getUploadId()); uploadPartRequest.setInputStream(fis); uploadPartRequest.setPartSize(size); uploadPartRequest.setPartNumber(i + 1); UploadPartResult uploadPartResult = client.uploadPart(uploadPartRequest); // 將返回的PartETag保存到List中。 partETags.add(uploadPartResult.getPartETag()); // 關閉文件 fis.close(); }
上面程序的核心是調用 uploadPart 方法來上傳每一個分塊,但是要注意以下幾點:
- uploadPart 方法要求除最后一個Part以外,其他的Part大小都要大於5MB。但是Upload Part接口並不會立即校驗上傳Part的大小(因為不知道是否為最后一塊);只有當Complete Multipart Upload的時候才會校驗。
- OSS會將服務器端收到Part數據的MD5值放在ETag頭內返回給用戶。為了保證數據在網絡傳輸過程中不出現錯誤,強烈推薦用戶在收到OSS的返回請求后,用該MD5值驗證上傳數據的正確性。
- Part號碼的范圍是1~10000。如果超出這個范圍,OSS將返回InvalidArgument的錯誤碼。
- 每次上傳part時都要把流定位到此次上傳塊開頭所對應的位置。
- 每次上傳part之后,OSS的返回結果會包含一個 PartETag 對象,他是上傳塊的ETag與塊編號(PartNumber)的組合,在后續完成分塊上傳的步驟中會用到它,因此我們需要將其保存起來。一般來講我們將這些 PartETag 對象保存到List中。
3. 完成分塊上傳
完成分塊上傳很簡單,如下:
CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, key, initiateMultipartUploadResult.getUploadId(), partETags); // 完成分塊上傳 CompleteMultipartUploadResult completeMultipartUploadResult = client.completeMultipartUpload(completeMultipartUploadRequest); // 打印Object的ETag System.out.println(completeMultipartUploadResult.getETag());
上面代碼中的 partETags 就是第二部中保存的partETag的列表,OSS收到用戶提交的Part列表后,會逐一驗證每個數據Part的有效性。當所有的數據Part驗證通過后,OSS將把這些數據part組合成一個完整的Object。
completeMultipartUpload 方法的返回結果中會包含拼裝后Object的ETag,用戶可以和本地文件的MD5值進行校驗以保證數據的有效性。
取消分塊上傳事件
我們可以用 abortMultipartUpload 方法取消分塊上傳。
AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucketName, key, uploadId); // 取消分塊上傳 client.abortMultipartUpload(abortMultipartUploadRequest);
獲取Bucket內所有分塊上傳事件
我們可以用 listMultipartUploads 方法獲取Bucket內所有上傳事件。
// 獲取Bucket內所有上傳事件
MultipartUploadListing listing = client.listMultipartUploads(listMultipartUploadsRequest); // 遍歷所有上傳事件 for (MultipartUpload multipartUpload : listing.getMultipartUploads()) { System.out.println("Key: " + multipartUpload.getKey() + " UploadId: " + multipartUpload.getUploadId()); }
Note
默認情況下,如果Bucket中的分塊上傳事件的數量大於1000,則只會返回1000個Object, 且返回結果中 IsTruncated 為 false,並返回 NextKeyMarker 和 NextUploadMarker 作為下此讀取的起點。若想增大返回分塊上傳事件數目,可以修改 MaxUploads 參數,或者使用 KeyMarker 以及 UploadIdMarker 參數分次讀取。
獲取所有已上傳的塊信息
我們可以用 listParts 方法獲取某個上傳事件所有已上傳的塊。
ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, key, uploadId); // 獲取上傳的所有Part信息 PartListing partListing = client.listParts(listPartsRequest); // 遍歷所有Part for (PartSummary part : partListing.getParts()) { System.out.println("PartNumber: " + part.getPartNumber() + " ETag: " + part.getETag()); }
Note
默認情況下,如果Bucket中的分塊上傳事件的數量大於1000,則只會返回1000個Object, 且返回結果中 IsTruncated 為 false,並返回 NextPartNumberMarker 作為下此讀取的起點。若想增大返回分塊上傳事件數目,可以修改 MaxParts 參數,或者使用 PartNumberMarker 參數分次讀取。
生成預簽名URL
如果你想把自己的資源發放給第三方用戶訪問,但是又不想開放Bucket的讀權限,可以通過生成預簽名URL的形式提供給用戶一個臨時的訪問URL。在生成URL時,你可以指定URL過期的時間,從而限制用戶長時間訪問。
生成一個預簽名的URL
如下代碼:
String bucketName = "your-bucket-name"; String key = "your-object-key"; // 設置URL過期時間為1小時 Date expiration = new Date(new Date().getTime() + 3600 * 1000); // 生成URL URL url = client.generatePresignedUrl(bucketName, key, expiration);
生成的URL默認以GET方式訪問,這樣,用戶可以直接通過瀏覽器訪問相關內容。
生成其他Http方法的URL
如果你想允許用戶臨時進行其他操作(比如上傳,刪除Object),可能需要簽名其他方法的URL,如下:
// 生成PUT方法的URL
URL url = client.generatePresignedUrl(bucketName, key, expiration, HttpMethod.PUT);
通過傳入 HttpMethod.PUT 參數,用戶可以使用生成的URL上傳Object。
添加用戶自定義參數(UserMetadata)
如果你想使用簽名的URL上傳Object,並指定UserMetadata等參數,可以這樣做:
// 創建請求
GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, key); // HttpMethod為PUT generatePresignedUrlRequest.setMethod(HttpMethod.PUT); // 添加UserMetadata generatePresignedUrlRequest.addUserMetadata("key", "value"); // 生成預簽名的URL URL url = client.generatePresignedUrl(bucketName, key, expiration);
需要注意的是,上述過程只是生成了簽名的URL,你仍需要在request header中添加UserMetadata的信息。
關於如何在Http請求中設置UserMetadata等參數,可以參考 OSS REST API 文檔 中的相關內容。
異常
OSS Java SDK 中有兩種異常 ClientException 以及 OSSException , 他們都繼承自或者間接繼承自 RuntimeException 。
ClientException
ClientException指SDK內部出現的異常,比如未設置BucketName,網絡無法到達等等。
OSSException
OSSException指服務器端錯誤,它來自於對服務器錯誤信息的解析。OSSException一般有以下幾個成員:
- Code: OSS返回給用戶的錯誤碼。
- Message: OSS給出的詳細錯誤信息。
- RequestId: 用於唯一標識該次請求的UUID;當你無法解決問題時,可以憑這個RequestId來請求OSS開發工程師的幫助。
- HostId: 用於標識訪問的OSS集群(目前統一為oss.aliyuncs.com)
下面是OSS中常見的異常:
錯誤碼 | 描述 |
---|---|
AccessDenied | 拒絕訪問 |
BucketAlreadyExists | Bucket已經存在 |
BucketNotEmpty | Bucket不為空 |
EntityTooLarge | 實體過大 |
EntityTooSmall | 實體過小 |
FileGroupTooLarge | 文件組過大 |
FilePartNotExist | 文件Part不存在 |
FilePartStale | 文件Part過時 |
InvalidArgument | 參數格式錯誤 |
InvalidAccessKeyId | Access Key ID不存在 |
InvalidBucketName | 無效的Bucket名字 |
InvalidDigest | 無效的摘要 |
InvalidObjectName | 無效的Object名字 |
InvalidPart | 無效的Part |
InvalidPartOrder | 無效的part順序 |
InvalidTargetBucketForLogging | Logging操作中有無效的目標bucket |
InternalError | OSS內部發生錯誤 |
MalformedXML | XML格式非法 |
MethodNotAllowed | 不支持的方法 |
MissingArgument | 缺少參數 |
MissingContentLength | 缺少內容長度 |
NoSuchBucket | Bucket不存在 |
NoSuchKey | 文件不存在 |
NoSuchUpload | Multipart Upload ID不存在 |
NotImplemented | 無法處理的方法 |
PreconditionFailed | 預處理錯誤 |
RequestTimeTooSkewed | 發起請求的時間和服務器時間超出15分鍾 |
RequestTimeout | 請求超時 |
SignatureDoesNotMatch | 簽名錯誤 |
TooManyBuckets | 用戶的Bucket數目超過限制 |
作者:王超 原文:http://aliyun_portal_storage.oss.aliyuncs.com/oss_api/oss_javahtml/index.html