OSS JAVA SDK

03/19 18:24
阅读数 134

安装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

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部