Appearance
概述
本文档主要介绍FOS Java SDK的安装和使用。在使用本文档前,您需要先了解FOS的一些基本知识,并已经开通了FOS服务。若您还不了解FOS,可以参考"产品描述"和"快速入门"。
安装SDK工具包
运行环境
Java SDK工具包可在JDK1.7、JDK1.8环境下运行(JDK9及以上版本已将tools.jar移除,JDK9及以上版本若需要使用FOS JavaSDK需要在lib下自行添加tools.jar)。
安装SDK
方式一:使用Maven安装 在Maven的pom.xml文件中添加fce-java-sdk的依赖:
xml
<dependency>
<groupId>com.flyme.yun</groupId>
<artifactId>fce-java-sdk</artifactId>
<version>{version}</version>
</dependency>
其中,{version}为版本号,可以 SDK仓库 页面找到。
SDK目录结构
com.flymefce
├── auth //FCE签名相关类
├── http //FCE的Http通信相关类
├── internal //SDK内部类
├── model //FCE公用model类
├── services
│ └─ fos //FOS服务相关类
│ ├─ model //FOS内部model,如Request或Response
│ ├─ FosClient.class //FOS客户端入口类
│ └─ FosClientConfiguration.class //针对FOS特有的HttpClient的配置
├── util //FCE公用工具类
├── FceClientConfiguration.class //对FCE的HttpClient的配置
├── FceClientException.class //FCE客户端的异常类
├── FceServiceException.class //与FCE服务端交互后的异常类
├── ErrorCode.class //FCE通用的错误码
└── Region.class //FCE提供服务的区域
卸载SDK
预期卸载 SDK 时,删除 pom 依赖或源码即可。
初始化
确认Endpoint
目前暂时支持一个Endpoint,对应信息为:
https://fos.flymeyun.com
获取密钥
要使用Flyme云FOS,您需要拥有一个有效的AK(Access Key ID)和SK(Secret Access Key)用来进行签名认证。AK/SK是由系统分配给用户的,均为字符串,用于标识用户,为访问FOS做签名验证。 目前情况下,请联系售后人员获取可使用的密钥列表。
新建FOSClient
FosClient是FOS服务的客户端,为开发者与FOS服务进行交互提供了一系列的方法。
使用AK/SK新建FosClient
通过AK/SK方式访FOS,用户可以参考如下代码新建一个FosClient:
java
public class Sample {
public static void main(String[] args) {
String ACCESS_KEY_ID = <your-access-key-id>; // 用户的Access Key ID
String SECRET_ACCESS_KEY = <your-secret-access-key>; // 用户的Secret Access Key
String ENDPOINT = <domain-name>; // 指定域名
// 初始化一个FosClient
FosClientConfiguration config = new FosClientConfiguration();
config.setCredentials(new DefaultFceCredentials(ACCESS_KEY_ID, SECRET_ACCESS_KEY));
config.setEndpoint(ENDPOINT);
config.setPathStyleAccessEnable(true);
FosClient client = new FosClient(config);
}
}
在上面代码中,ACCESS_KEY_ID对应控制台中的“Access Key ID”,SECRET_ACCESS_KEY对应控制台中的“Access Key Secret”,获取方式请参考控制台"快速上手"。
配置FosClient
如果用户需要配置FosClient的一些细节的参数,可以在构造FosClient的时候传入FosClientConfiguration对象。 FosClientConfiguration是FOS服务的配置类,可以为客户端配置代理,最大连接数等参数。
使用代理
下面一段代码可以让客户端使用代理访问FOS服务:
java
String ACCESS_KEY_ID = <your-access-key-id>; // 用户的Access Key ID
String SECRET_ACCESS_KEY = <your-secret-access-key>; // 用户的Secret Access Key
String ENDPOINT = <domain-name>; // 指定域名
// 初始化一个FosClient
FosClientConfiguration config = new FosClientConfiguration();
// 配置代理为本地8080端口
config.setProxyHost("127.0.0.1");
config.setProxyPort(8080);
// 创建FOS客户端
config.setCredentials(new DefaultFceCredentials(ACCESS_KEY_ID, SECRET_ACCESS_KEY));
config.setEndpoint(ENDPOINT);
config.setPathStyleAccessEnable(true);
FosClient client = new FosClient(config);
使用上面的代码段,客户端的所有操作都会通过127.0.0.1地址的8080端口做代理执行。 对于有用户验证的代理,可以通过下面的代码段配置用户名和密码:
java
// 创建FosClientConfiguration实例
FosClientConfiguration config = new FosClientConfiguration();
// 配置代理为本地8080端口
config.setProxyHost("127.0.0.1");
config.setProxyPort(8080);//设置用户名和密码
config.setProxyUsername(<username>); //用户名
config.setProxyPassword(<password>); //密码
设置网络参数
用户可以用FosClientConfiguration对基本网络参数进行设置:
java
FosClientConfiguration config = new FosClientConfiguration();
// 设置HTTP最大连接数为10
config.setMaxConnections(10);
// 设置TCP连接超时为5000毫秒
config.setConnectionTimeoutInMillis(5000);
// 设置Socket传输数据超时的时间为2000毫秒
config.setSocketTimeoutInMillis(2000);
设置访问域名风格
SDK会默认自动开启bucket virtual hosting,可以通过以下配置开启使用PathStyle风格的endpoint:
java
FosClientConfiguration config = new FosClientConfiguration();
// 开启使用PathStyle
config.setPathStyleAccessEnable(true);
注意:部分非FOS常规域名下,默认bucket virtual hosting风格的可能会有不兼容场景,表现为dns解析失败等,可以开启PathStyle解决。 其他场景建议使用默认风格。
设置同步PUT
对PUT操作,默认使用CloseableHttpAsyncClient,可能会出现用户进程执行完未退出的现象。用户可以通过FosClientConfiguration设置所有FOS请求都通过FosClient同步的方式:
java
FosClientConfiguration config = new FosClientConfiguration();
// 设置PUT操作为同步方式,默认异步
config.setEnableHttpAsyncPut(false);
参数说明
通过FosClientConfiguration能指定的所有参数如下表所示:
参数 | 说明 |
---|---|
CnameEnabled | 使用cname访问FOS资源 |
ConnectionTimeoutInMillis | 建立连接的超时时间(单位:毫秒) |
Credentials | 客户端用于签署HTTP请求的FCE凭据 |
EnableHttpAsyncPut | 异步put |
Endpoint | 访问域名 |
LocalAddress | 本地地址 |
MaxConnections | 允许打开的最大HTTP连接数 |
Protocol | 连接协议类型 |
ProxyDomain | 访问NTLM验证的代理服务器的Windows域名 |
ProxyHost | 代理服务器主机地址 |
ProxyPassword | 代理服务器验证的密码 |
ProxyPort | 代理服务器端口 |
ProxyPreemptiveAuthenticationEnabled | 是否设置用户代理认证 |
ProxyUsername | 代理服务器验证的用户名 |
ProxyWorkstation | NTLM代理服务器的Windows工作站名称 |
Region | 地域 |
RetryPolicy | 连接重试策略 |
SocketBufferSizeInBytes | Socket缓冲区大小 |
SocketTimeoutInMillis | 通过打开的连接传输数据的超时时间(单位:毫秒) |
StreamBufferSize | 流文件缓冲区大小 |
UserAgent | 用户代理,指HTTP的User-Agent头 |
RedirectsEnabled | 是否开启HTTP重定向。默认开启 |
PathStyleAccessEnable | 是否使用PathStyle风格的endpoint,默认关闭,即使用bucket virtual hosting |
Bucket 管理
概述
Bucket既是FOS上的命名空间,也是计费、权限控制、日志记录等高级功能的管理实体。
- Bucket名称具有全局唯一性,且不能修改。
- 存储在FOS上的每个Object都必须包含在一个Bucket中。
- 一个用户最多可创建100个Bucket。但每个Bucket中存放的Object的数量和大小总和没有限制,用户不需要考虑数据的可扩展性。
Bucket权限管理
设置Bucket的访问权限
如下代码将Bucket的权限设置为了private。
java
public void setBucketPrivate (FosClient client, String bucketName) {
client.setBucketAcl(<bucketName>, CannedAccessControlList.Private);}
CannedAccessControlList是枚举类型,包含三个值: Private 、 PublicRead 、 PublicReadWrite ,它们分别对应相关权限。具体内容可以参考FOS控制台"快速上手"。
设置更多Bucket访问权限
- 通过设置refer白名单方式设置防盗链
java
String jsonAcl = "";
client.setBucketAcl("bucketName", jsonAcl)
其中jsonAcl为{"accessControlList":["+ "{"grantee":[{"id":"*"}], "+ ""permission":["FULL_CONTROL"], "+ ""condition":{"referer":{"stringEquals":["http://test/index\"]}" + "}}]}
- 限制客户端IP访问,只允许部分客户端IP访问
java
String jsonAcl = "";
client.setBucketAcl("bucketName", jsonAcl)
其中jsonAcl为{"accessControlList":["+ "{"grantee":[{"id":"*"}], "+ ""permission":["FULL_CONTROL"], "+ ""condition":{"ipAddress":["192.170.0.6"]" + "}}]}"}
查看Bucket的权限
如下代码可以查看Bucket的权限:
java
GetBucketAclResponse aclResponse = client.getBucketAcl("bucketName");
System.out.println(aclResponse.getAccessControlList().toString());
getBucketAcl方法返回的解析类中可供调用的参数有:
参数 | 说明 |
---|---|
owner | Bucket owner信息 |
id | Bucket owner的用户ID |
acl | 标识Bucket的权限列表 |
grantee | 标识被授权人 |
-id | 被授权人ID |
permission | 标识被授权人的权限 |
新建Bucket
如下代码可以新建一个Bucket:
java
public void createBucket (FosClient client, String bucketName) {
// 新建一个Bucket
client.createBucket(<bucketName>); //指定Bucket名称
}
注意: 由于Bucket的名称是唯一的,所以需要保证bucketName不与其他的Bucket名称相同。 Bucket的命名有以下规范:
- 只能包括小写字母,数字,短横线(-)。
- 必须以小写字母或者数字开头。
- 长度必须在4-63字节之间。
通过上述代码创建的bucket,权限是私有读写,存储类型是标准类型(Standard)。
列举Bucket
如下代码可以列出用户所有的Bucket。
java
public void listBuckets (FosClient client) {
// 获取用户的Bucket列表
List<BucketSummary> buckets = client.listBuckets().getBuckets();
// 遍历Bucket
for (BucketSummary bucket : buckets) {
System.out.println(bucket.getName());
}
}
删除Bucket
如下代码可以删除一个Bucket。
java
public void deleteBucket (FosClient client, String bucketName) {
// 删除Bucket
client.deleteBucket(<bucketName>); //指定Bucket名称
}
注意:
- 在删除前需要保证此Bucket下的所有Object和未完成的三步上传Part已经被删除,否则会删除失败。
- 在删除前确认该Bucket没有开通跨区域复制,不是跨区域复制规则中的源Bucket或目标Bucket,否则不能删除。
判断Bucket是否存在
若用户需要判断某个Bucket是否存在,则如下代码可以做到。
java
public void doesBucketExist (FosClient client, String bucketName) {
// 获取Bucket的存在信息
// 指定Bucket名称
boolean exists = client.doesBucketExist(bucketName);
// 输出结果
if (exists) {
System.out.println("Bucket exists");
} else {
System.out.println("Bucket not exists");
}
}
文件管理
上传文件
在FOS中,用户操作的基本数据单元是Object。Object包含Key、Meta和Data。其中,Key是Object的名字;Meta是用户对该Object的描述,由一系列Name-Value对组成;Data是Object的数据。 FOS Java SDK提供了丰富的文件上传接口,可以通过以下方式上传文件:
- 简单上传
- 追加上传
- 分片上传
- 断点续传上传
简单上传
FOS在简单上传的场景中,支持以指定文件形式、以数据流方式、以二进制串方式、以字符串方式执行Object上传。
java
public void PutObject(FosClient client, String bucketName, String objectKey, byte[] byte1, String string1){
// 获取指定文件
File file = new File("/path/to/file.zip");
// 获取数据流
InputStream inputStream = new FileInputStream("/path/to/test.zip");
// 以文件形式上传
PutObjectResponse putObjectFromFileResponse = client.putObject(bucketName, objectKey, file);
// 以数据流形式上传
PutObjectResponse putObjectResponseFromInputStream = client.putObject(bucketName, objectKey, inputStream);
// 以二进制串上传
PutObjectResponse putObjectResponseFromByte = client.putObject(bucketName, objectKey, byte1);
// 以字符串上传
PutObjectResponse putObjectResponseFromString = client.putObject(bucketName, objectKey, string1);
// 创建空目录,objectKey需要以正斜线结尾,例如test/
// FOS控制台默认会将以正斜线(/)结尾的对象,作为文件目录的形式展示
PutObjectResponse putObjectResponseFromString = client.putObject(bucketName, objectKey, "");
// 打印ETag
System.out.println(putObjectFromFileResponse.getETag());
}
Object以文件的形式上传到FOS中,PutObject函数支持不超过100M的Object上传。在PutObject请求处理成功后,FOS会在Header中返回Object的ETag作为文件标识。 设置文件元信息 文件元信息(Object Meta),是对用户在向FOS上传文件时,同时对文件进行的属性描述,主要分为分为两种:设置HTTP标准属性(HTTP Headers)和用户自定义的元信息。
- 设定Object的Http Header
FOS Java SDK本质上是调用后台的HTTP接口,因此用户可以在上传文件时自定义Object的Http Header。常用的http header说明如下:
名称 | 描述 | 默认值 |
---|---|---|
Content-MD5 | 文件数据校验,设置后FOS会启用文件内容MD5校验,把您提供的MD5与文件的MD5比较,不一致会抛出错误 | 无 |
Content-Type | 文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如没有指,FOS则根据文件的扩展名自动生成,如文件没有扩展名则填默认值 | application/octet-stream |
Content-Disposition | 指示MIME用户代理如何显示附加的文件,打开或下载,及文件名称 | 无 |
Content-Length | 上传的文件的长度,超过流/文件的长度会截断,不足为实际值 | 流/文件时间长度 |
Expires | 缓存过期时间 | 无 |
Cache-Control | 指定该Object被下载时的网页的缓存行为 | 无 |
x-ic-content-crc32 | 上传object的CRC值(循环冗余校验码) | 无 |
参考代码如下:
java
// 初始化上传输入流
ObjectMetadata meta = new ObjectMetadata();
// 设置ContentLength大小
meta.setContentLength(1000);
// 设置ContentType
meta.setContentType("application/json");
// 设置cache-control
meta.setCacheControl("no-cache");
// 设置x-ic-content-crc32
meta.setxIcCrc("crc");
client.putObject(bucketName, objectKey, content, meta);
- 用户自定义元信息
FOS支持用户自定义元数据来对Object进行描述。如下代码所示:
java
// 设置自定义元数据name的值为my-data
meta.addUserMetadata("name", "my-data");
// 上传Object
client.putObject(bucketName, objectKey, content, meta);
提示:
- 在上面代码中,用户自定义了一个名字为”name”,值为”my-data”的元数据
- 当用户下载此Object的时候,此元数据也可以一并得到
- 一个Object可以有多个类似的参数,但所有的User Meta总大小不能超过2KB
追加上传
上文介绍的简单上传方式,创建的Object都是Normal类型,用户不可再进行追加写,这在日志、视频监控、视频直播等数据复写较频繁的场景中使用不方便。 正因如此,Flyme云FOS特别支持了AppendObject,即以追加写的方式上传文件。通过AppendObject操作创建的Object类型为Appendable Object,可以对该Object追加数据。AppendObject大小限制为0~5G。 通过AppendObject方式上传示例代码如下
java
public void AppendObject(FosClient client, String bucketName, String objectKey, byte[] byte1, String string1) {
// 获取指定文件
File file = new File("/path/to/file.zip");
// 获取数据流
InputStream inputStream = new FileInputStream("/path/to/test.zip");
// 以文件形式上传
ObjectAppendObjectResponse appendObjectFromFileResponse = client.appendObject(bucketName, objectKey, file);
// 以数据流形式上传
ObjectAppendObjectResponse appendObjectResponseFromInputStream = client.appendObject(bucketName, objectKey, inputStream);
// 以二进制串上传
ObjectAppendObjectResponse appendObjectResponseFromByte = client.appendObject(bucketName, objectKey, byte1);
// 以字符串上传
ObjectAppendObjectResponse appendObjectResponseFromString = client.appendObject(bucketName, objectKey, string1);
// 打印ETag
System.out.println(appendObjectFromFileResponse.getETag());
// 打印NextAppendOffset
System.out.println(appendObjectFromFileResponse.getNextAppendOffset());
// 打印ContentMd5
System.out.println(appendObjectFromFileResponse.getContentMd5());
// 追加上传的示例,需要在请求中加上下次追加写的位置
Long nextAppendOffset = appendObjectFromFileResponse.getNextAppendOffset();
AppendObjectRequest appendObjectFromFileRequest = new AppendObjectRequest(bucketName,objectKey,file);
appendObjectFromFileRequest.setOffset(nextAppendOffset);
AppendObjectResponse appendObjectFromFileResponse = client.appendObject(appendObjectFromFileRequest);
}
注意:
- 追加上传需要指定追加写的位置,否则已写数据会被覆盖
- 不支持并发,如果确实有多客户端写入的需求,需要自行维护追加写位置,避免冲突
分块上传
除了通过简单上传及追加上传方式将文上传到FOS以外,FOS还提供了另外一种上传模式 —— Multipart Upload。用户可以在如下的应用场景内(但不仅限于此),使用Multipart Upload上传模式,如:
- 需要支持断点上传。
- 上传超过100M大小的文件。
- 网络条件较差,和FOS的服务器之间的连接经常断开。
- 需要流式地上传文件。
- 上传文件之前,无法确定上传文件的大小。
下面将一步步介绍Multipart Upload的实现。假设有一个文件,本地路径为 /path/to/file.zip ,由于文件比较大,将其分块传输到FOS中。
初始化Multipart Upload
使用 initiateMultipartUpload 方法来初始化一个分块上传事件:
java
// 开始Multipart Upload
InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucketName, objectKey);
InitiateMultipartUploadResponse initiateMultipartUploadResponse = client.initiateMultipartUpload(initiateMultipartUploadRequest);
// 打印UploadId
System.out.println("UploadId: " + initiateMultipartUploadResponse.getUploadId());
initiateMultipartUpload 的返回结果中含有 UploadId ,它是区分分块上传事件的唯一标识,在后面的操作中,我们将用到它。
- 上传低频存储类型Object的初始化
初始化低频存储的一个分块上传事件:
java
public void putMultiUploadStorageClass(){
InitiateMultipartUploadRequest iniReq = new InitiateMultipartUploadRequest(bucketName, key);
iniReq.withStorageClass(FosClient.STORAGE_CLASS_STANDARD_IA);
client.initiateMultipartUpload(iniReq);
}
- 上传冷存储类型Object的初始化
初始化低频存储的一个分块上传事件:
java
public void putMultiUploadStorageClass(){
InitiateMultipartUploadRequest iniReq = new InitiateMultipartUploadRequest(bucketName, key);
iniReq.withStorageClass(FosClient.STORAGE_CLASS_COLD);
client.initiateMultipartUpload(iniReq);
}
上传分块
接着,把文件分块上传。
java
// 设置每块为 5MB
final long partSize = 1024 * 1024 * 5L;
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(objectKey);
uploadPartRequest.setUploadId(initiateMultipartUploadResponse.getUploadId());
uploadPartRequest.setInputStream(fis);
uploadPartRequest.setPartSize(size);
uploadPartRequest.setPartNumber(i + 1);
UploadPartResponse uploadPartResponse = client.uploadPart(uploadPartRequest);
// 将返回的PartETag保存到List中。
partETags.add(uploadPartResponse.getPartETag());
// 关闭文件
fis.close();
}
上面代码的核心是调用 UploadPart 方法来上传每一个分块,但是要注意以下几点:
- UploadPart 方法要求除最后一个Part以外,其他的Part大小都要大于等于100KB。但是Upload Part接口并不会立即校验上传Part的大小;只有当Complete Multipart Upload的时候才会校验。
- 为了保证数据在网络传输过程中不出现错误,建议您在UploadPart后,使用每个分块FOS返回的Content-MD5值分别验证已上传分块数据的正确性。当所有分块数据合成一个Object后,不再含MD5值。
- Part号码的范围是1~10000。如果超出这个范围,FOS将返回InvalidArgument的错误码。
- 每次上传Part时都要把流定位到此次上传块开头所对应的位置。
- 每次上传Part之后,FOS的返回结果会包含一个 PartETag 对象,它是上传块的ETag与块编号(PartNumber)的组合,在后续完成分块上传的步骤中会用到它,因此需要将其保存起来。一般来讲这些 PartETag 对象将被保存到List中。
完成分块上传
如下代码所示,完成分块上传:
java
CompleteMultipartUploadRequest completeMultipartUploadRequest =new CompleteMultipartUploadRequest(bucketName, objectKey, initiateMultipartUploadResponse.getUploadId(), partETags);
// 完成分块上传
CompleteMultipartUploadResponse completeMultipartUploadResponse =
client.completeMultipartUpload(completeMultipartUploadRequest);
// 打印Object的ETag
System.out.println(completeMultipartUploadResponse.getETag());
上面代码中的 partETags 是第二部中保存的partETag的列表,FOS收到用户提交的Part列表后,会逐一验证每个数据Part的有效性。当所有的数据Part验证通过后,FOS将把这些数据part组合成一个完整的Object。
取消分块上传事件
用户可以使用abortMultipartUpload方法取消分块上传。
java
AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucketName, objectKey, uploadId);
// 取消分块上传
client.abortMultipartUpload(abortMultipartUploadRequest);
获取未完成的分块上传事件
用户可以使用 listMultipartUploads 方法获取Bucket内未完成的分块上传事件。
java
ListMultipartUploadsRequest listMultipartUploadsRequest =
new ListMultipartUploadsRequest(bucketName);
// 获取Bucket内所有上传事件
ListMultipartUploadsResponse listing = client.listMultipartUploads(listMultipartUploadsRequest);
// 遍历所有上传事件
for (MultipartUploadSummary multipartUpload : listing.getMultipartUploads()) {
System.out.println("Key: " + multipartUpload.getKey() + " UploadId: " + multipartUpload.getUploadId());
}
注意:
- 默认情况下,如果Bucket中的分块上传事件的数目大于1000,则只会返回1000个Object,并且返回结果中IsTruncated的值为True,同时返回NextKeyMarker作为下次读取的起点。
- 若想返回更多分块上传事件的数目,可以使用KeyMarker参数分次读取。
获取所有已上传的块信息
用户可以使用 listParts 方法获取某个上传事件中所有已上传的块。
java
ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, objectKey, uploadId);
// 获取上传的所有Part信息
ListPartsResponse partListing = client.listParts(listPartsRequest);
// 遍历所有Part
for (PartSummary part : partListing.getParts()) {
System.out.println("PartNumber: " + part.getPartNumber() + " ETag: " + part.getETag());
}
如果需要查看Object的存储类型storage class使用以下代码:
java
public void listPartsStorageClass(){
ListResponse listPartsResponse = client.listParts(bucketName, key, uploadId);
String storageClass = listPartsResponse.getStorageClass();
}
注意:
- 默认情况下,如果Bucket中的分块上传事件的数目大于1000,则只会返回1000个Object,并且返回结果中IsTruncated的值为True,同时返回NextPartNumberMarker作为下次读取的起点。
- 若想返回更多分块上传事件的数目,可以使用PartNumberMarker参数分次读取。
封装分块上传
在Java SDK中,Fos为用户提供了putSuperObjectFromFile接口,它对分块上传涉及到的initiateMultipartUpload、UploadPart、completeMultipartUpload三个方法进行封装,用户只需调用该接口即可完成分块上传,
java
File file = new File("/path/to/file.zip");
PutSuperObjectRequest request = new PutSuperObjectRequest(bucketName, objectKey, file);
FosClient.putSuperObjectFromFile(request);
其中PutSuperObjectRequest的参数有:
参数 | 说明 |
---|---|
chunkSize | 分块大小,默认为5MB,不能小于100KB |
nThreads | 分块上传中线程池中线程的数量,默认等于CPU的核数 |
isSuperObjectUploadCanced | 是否取消分块上传 |
File | 上传文件 |
若一个大文件耗时很长,用户想结束分块上传,可调用PutSuperObjectRequest中的cancel()方法设置isSuperObjectUploadCanced为true实现取消分块上传操作。
断点续传上传
当用户向FOS上传大文件时,如果网络不稳定或者遇到程序崩等情况,则整个上传就失败了,失败前已经上传的部分也作废,用户不得不重头再来。这样做不仅浪费资源,在网络不稳定的情况下,往往重试多次还是无法完成上传。 基于上述场景,FOS提供了断点续传上传的能力:
- 当网络情况一般的情况下,建议使用三步上传方式,将object分为5Mb的块,参考分块上传。
- 当您的网络情况非常差,推荐使用appendObject的方式进行断点续传,每次append 较小数据256kb,参考追加上传。
提示
- 断点续传是分片上传的封装和加强,是用分片上传实现的;
- 文件较大或网络环境较差时,推荐使用分片上传;
下载文件
FOS Java SDK提供了丰富的文件下载接口,用户可以通过以下方式从FOS中下载文件:
- 简单流式下载
- 下载到本地文件
- 断点续传下载
- 范围下载
- 下载进度条
简单流式下载
用户可以通过如下代码将Object读取到一个流中:
java
public void getObject(FosClient client, String bucketName, String objectKey)
throws IOException {
// 获取Object,返回结果为FosObject对象
FosObject object = client.getObject(bucketName, objectKey);
// 获取ObjectMeta
ObjectMetadata meta = object.getObjectMetadata();
// 获取Object的输入流
InputStream objectContent = object.getObjectContent();
// 处理Object
...
// 关闭流
objectContent.close();
}
注意:
- FosObject中包含了Object的各种信息,包含Object所在的Bucket、Object的名称、MetaData以及一个输入流,用户可以通过操作输入流将Object的内容读取到文件或者内存中。
- ObjectMetadata中包含了Object上传时定义的ETag,Http Header以及自定义的元数据。
- 通过FosObject的getObjectContent方法,还可以获取返回Object的输入流,用户可以读取这个输入流来对Object的内容进行操作。
直接下载Object到文件
用户可以通过如下代码直接将Object下载到指定文件:
java
// 新建GetObjectRequest
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectKey);
// 下载Object到文件
ObjectMetadata objectMetadata = client.getObject(getObjectRequest, new File("/path/to/file","filename"));
当使用上面方法将Object直接下载到文件时,方法返回ObjectMetadata对象。
范围下载
为了实现更多的功能,可以通过使用GetObjectRequest来指定下载范围,实现更精细化地获取Object。如果指定的下载范围是0 - 100,则返回第0到第100个字节的数据,包括第100个,共101字节的数据,即[0, 100]。
java
// 新建GetObjectRequest
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectKey);
// 获取0~100字节范围内的数据
getObjectRequest.setRange(0, 100);
// 获取Object,返回结果为FosObject对象
FosObject object = client.getObject(getObjectRequest);
通过getObjectRequest的setRange方法可以设置返回Object的范围。用户也可以用此功能实现文件的分段下载和断点续传。
其他使用方法
获取Object的存储类型
Object的storage class属性分为STANDARD(标准存储), STANDARD_IA(低频存储)和COLD(冷存储),通过如下代码可以实现:
java
public void getObjectStorageClass(){
ObjectMetadata meta = client.getObjectMetadata(bucketName, key);
String storageClass = meta.getStorageClass();
}
只获取ObjectMetadata
通过 getObjectMetadata 方法可以只获取ObjectMetadata而不获取Object的实体。如下代码所示:
java
ObjectMetadata objectMetadata = client.getObjectMetadata(bucketName, objectKey);
getObjectMetadata方法返回的解析类中可供调用的参数有:
参数 | 说明 |
---|---|
contentType | Object的类型 |
contentLength | Object的大小 |
contentMd5 | Object的MD5 |
etag | Object的HTTP协议实体标签 |
storageClass | Object的存储类型 |
userMetadata | 如果在PutObject指定了userMetadata自定义meta,则返回此项 |
xIcCrc | 如果在PutObject指定了object的CRC值(循环冗余校验码),则返回此项 |
获取文件下载URL
用户可以通过如下代码获取指定Object的URL:
java
public String generatePresignedUrl(FosClient client, String bucketName, String objectKey, int expirationInSeconds) {
//指定用户需要获取的Object所在的Bucket名称、该Object名称、URL的有效时长
URL url = client.generatePresignedUrl(<bucketName>, <objectKey>, <expirationInSeconds>);
return url.toString();
}
说明:
- expirationInSeconds为指定的URL有效时长,时间从当前时间算起,为可选参数,不配置时系统默认值为1800秒。如果要设置为永久不失效的时间,可以将expirationInSeconds参数设置为 -1,不可设置为其他负数。
- 如果预期获取的文件时公共可读的,则对应URL链接可通过简单规则快速拼接获取: http://$bucketName.$region.flyme.com/$objectKey或者http://$region.flyme.com/$bucketName/$objectKey
列举存储空间中的文件
FOS SDK支持用户通过以下两种方式列举出object:
- 简单列举
- 通过参数复杂列举
除此之外,用户还可在列出文件的同时模拟文件夹
简单列举
当用户希望简单快速列举出所需的文件时,可通过listObjects方法返回ListObjectsResponse对象,ListObjectsResponse对象包含了此次listObject请求的返回结果。用户可以通过ListObjectsResponse中的getContents方法获取所有Object的描述信息。
java
public void listObjects(FosClient client, String bucketName) {
// 获取指定Bucket下的所有Object信息
ListObjectsResponse listing = client.listObjects(bucketName);
// 遍历所有Object
for (FosObjectSummary objectSummary : listing.getContents()) {
System.out.println("ObjectKey: " + objectSummary.getKey());
}
}
注意:
- 默认情况下,如果Bucket中的Object数量大于1000,则只会返回1000个Object,并且返回结果中IsTruncated值为True,并返回NextMarker做为下次读取的起点。
- 若想增大返回Object的数目,可以使用Marker参数分次读取。
通过参数复杂列举
除上述简单列举外,用户还可通过设置ListObjectsRequest的参数实现各种灵活的查询功能。ListObjectsRequest的可设置的参数如下:
参数 | 功能 | 使用方式 |
---|---|---|
Prefix | 限定返回的object key必须以prefix作为前缀 | setPrefix(String prefix) |
Delimiter | 是一个用于对Object名字进行分组的字符所有名字包含指定的前缀且第一次出现。Delimiter字符之间的Object作为一组元素: CommonPrefixes | setDelimiter(String delimiter) |
Marker | 设定结果从marker之后按字母排序的第一个开始返回 | setMarker(String marker) |
MaxKeys | 限定此次返回object的最大数,最大为1000,默认值是1000。如果指定的值大于1000,按1000操作 | setMaxKeys(int maxKeys) |
注意:
- 如果有Object以Prefix命名,当仅使用Prefix查询时,返回的所有Key中仍会包含以Prefix命名的Object。
- 如果有Object以Prefix命名,当使用Prefix和Delimiter组合查询时,返回的所有Key中会有Null,Key的名字不包含Prefix前缀。
下面我们分别以几个案例说明通过参数列举的方法: 指定最大返回条数
java
// 指定最大返回条数为500
ListObjectsRequest listObjectsRequest = new ListObjectsRequest("bucketName");
listObjectsRequest.withMaxKeys(500);
ListObjectsResponse listObjectsResponse = client.listObjects(listObjectsRequest);
for(FosObjectSummary objectSummary :listObjectsResponse.getContents()) {
System.out.println("ObjectKey:" + objectSummary.getKey());
}
返回指定前缀的object
java
// 指定返回前缀为test的object
ListObjectsRequest listObjectsRequest = new ListObjectsRequest("bucketName");
listObjectsRequest.withPrefix("test");
ListObjectsResponse listObjectsResponse = client.listObjects(listObjectsRequest);
for(FosObjectSummary objectSummary :listObjectsResponse.getContents()) {
System.out.println("ObjectKey:" + objectSummary.getKey());
}
从指定Object后返回
java
// 用户可以定义不包括某object,从其之后开始返回
ListObjectsRequest listObjectsRequest = new ListObjectsRequest("bucketName");
listObjectsRequest.withMarker("object");
ListObjectsResponse listObjectsResponse = client.listObjects(listObjectsRequest);
for(FosObjectSummary objectSummary :listObjectsResponse.getContents()) {
System.out.println("ObjectKey:" + objectSummary.getKey());
}
分页获取所有Object
java
// 用户可设置每页最多500条记录
ListObjectsRequest listObjectsRequest = new ListObjectsRequest("bucketName");
listObjectsRequest.withMaxKeys(500);
ListObjectsResponse listObjectsResponse;boolean isTruncated = true;
while (isTruncated) {
listObjectsResponse = client.listObjects(listObjectsRequest);
isTruncated = listObjectsResponse.isTruncated();
if (listObjectsResponse.getNextMarker() != null) {
listObjectsRequest.withMarker(listObjectsResponse.getNextMarker());
}
}
分页获取所有特定Object后的结果
java
// 用户可设置每页最多500条记录,并从某特定object之后开始获取
ListObjectsRequest listObjectsRequest = new ListObjectsRequest("bucketName");
listObjectsRequest.withMaxKeys(500);
listObjectsRequest.withMarker("object");
ListObjectsResponse listObjectsResponse;
boolean isTruncated = true;
while (isTruncated) {
listObjectsResponse = client.listObjects(listObjectsRequest);
isTruncated = listObjectsResponse.isTruncated();
if (listObjectsResponse.getNextMarker() != null) {
listObjectsRequest.withMarker(listObjectsResponse.getNextMarker());
}
}
分页获取所有指定前缀的Object结果
java
// 用户可设置分页获取指定前缀的Object,每页最多500条记录
ListObjectsRequest listObjectsRequest = new ListObjectsRequest("bucketName");
listObjectsRequest.withMaxKeys(500);
listObjectsRequest.withPrefix("object");
ListObjectsResponse listObjectsResponse;
boolean isTruncated = true;
while (isTruncated) {
listObjectsResponse = client.listObjects(listObjectsRequest);
isTruncated = listObjectsResponse.isTruncated();
if (listObjectsResponse.getNextMarker() != null) {
listObjectsRequest.withMarker(listObjectsResponse.getNextMarker());
}
}
listObject方法返回的解析类中可供调用的参数有:
参数 | 说明 |
---|---|
name | Bucket名称 |
prefix | 匹配以prefix开始到第一次出现Delimiter字符之间的object作为一组元素返回 |
marker | 本次查询的起点 |
maxKeys | 请求返回的最大数目 |
isTruncated | 指明是否所有查询都返回了;false-本次已经返回所有结果,true-本次还没有返回所有结果 |
contents | 返回的一个Object的容器 |
+key | Object名称 |
+lastModified | 此Object最后一次被修改的时间 |
+eTag | Object的HTTP协议实体便签 |
+storageClass | Object的存储形态 |
+size | Object的内容的大小(字节数) |
+owner | Object对应Bucket所属用户信息 |
++id | Bucket Owner的用户ID |
++displayName | Bucket Owner的名称 |
模拟文件夹功能
在FOS的存储结果中是没有文件夹这个概念的,所有元素都是以Object来存储,但FOS的用户在使用数据时往往需要以文件夹来管理文件。 因此,FOS提供了创建模拟文件夹的能力,其本质上来说是创建了一个size为0的Object。对于这个Object可以上传下载,只是控制台会对以”/“结尾的Object以文件夹的方式展示。 用户可以通过 Delimiter 和 Prefix 参数的配合模拟出文件夹功能。Delimiter 和 Prefix 的组合效果是这样的: 如果把 Prefix 设为某个文件夹名,就可以罗列以此 Prefix 开头的文件,即该文件夹下递归的所有的文件和子文件夹(目录)。文件名在Contents中显示。 如果再把 Delimiter 设置为 “/” 时,返回值就只罗列该文件夹下的文件和子文件夹(目录),该文件夹下的子文件名(目录)返回在 CommonPrefixes 部分,子文件夹下递归的文件和文件夹不被显示。 如下是几个应用方式: 列出Bucket内所有文件 当用户需要获取Bucket下的所有文件时,可以参考分页获取所有Object 递归列出目录下所有文件 可以通过设置 Prefix 参数来获取某个目录下所有的文件:
java
// 构造ListObjectsRequest请求
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);
// 递归列出fun目录下的所有文件
listObjectsRequest.setPrefix("fun/");
ListObjectsResponse listing = client.listObjects(listObjectsRequest);
// 遍历所有Object
System.out.println("Objects:");
for (FosObjectSummary objectSummary : listing.getContents()) {
System.out.println(objectSummary.getKey());
}
输出:
java
Objects:
fun/
fun/movie/001.avi
fun/movie/007.avi
fun/test.jpg
查看目录下的文件和子目录 在 Prefix 和 Delimiter 结合的情况下,可以列出目录下的文件和子目录:
java
// 构造ListObjectsRequest请求
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);
// "/" 为文件夹的分隔符
listObjectsRequest.setDelimiter("/");
// 列出fun目录下的所有文件和文件夹
listObjectsRequest.setPrefix("fun/");
ListObjectsResponse listing = client.listObjects(listObjectsRequest);
// 遍历所有Object
System.out.println("Objects:");
for (FosObjectSummary objectSummary : listing.getContents()) {
System.out.println(objectSummary.getKey());
}
// 遍历所有CommonPrefix
System.out.println("\nCommonPrefixs:");
for (String commonPrefix : listing.getCommonPrefixes()) {
System.out.println(commonPrefix);
}
输出:
java
Objects:
fun/
fun/test.jpg
CommonPrefixs:
fun/movie/
返回的结果中, ObjectSummaries 的列表中给出的是fun目录下的文件。而 CommonPrefixs 的列表中给出的是fun目录下的所有子文件夹。可以看出 fun/movie/001.avi , fun/movie/007.avi 两个文件并没有被列出来,因为它们属于 fun 文件夹下的 movie 目录。
Object权限控制
设置Object的访问权限
目前FOS支持两种方式设置ACL。第一种是使用Canned Acl,在PutObjectAcl的时候,通过头域的"x-ic-acl"或者"x-ic-grant-permission'来设置object访问权限,当前可设置的权限包括private和public-read,两种类型的header不可以同时在一个请求中出现。第二种方式是上传一个ACL文件。
- 通过使用头域的"x-ic-acl"或者"x-ic-grant-permission'来设置object访问权限
java
SetObjectAclRequest setObjectAclRequest = new SetObjectAclRequest("yourBucketName","objectKey",CannedAccessControlList.PublicRead);
client.setObjectAcl(setObjectAclRequest);
- 通过setObjectAcl设置object访问权限
java
String jsonObjectAcl = "{\"accessControlList\":["+ "{\"grantee\":[{\"id\":\"*\"}], "+ "\"permission\":[\"FULL_CONTROL\"]"+"}]}";
SetObjectAclRequest setObjectAclRequest = new SetObjectAclRequest("yourBucketName","objectKey",jsonObjectAcl);
client.setObjectAcl(setObjectAclRequest);
查看Object的权限
如下代码可以查Object的权限:
java
GetObjectAclRequest getObjectRequest = new GetObjectAclRequest();
getObjectRequest.withBucketName("yourBucketName");
getObjectRequest.withKey("objectKey");
GetObjectAclResponse response = client.getObjectAcl(getObjectRequest);
getObjectAcl方法返回的解析类中可供调用的参数有:
参数 | 说明 |
---|---|
accessControlList | 标识Object的权限列表 |
grantee | 标识被授权人 |
-id | 被授权人ID |
permission | 标识被授权人的权限 |
删除Object的权限
如下代码可以删除Object的权限:
java
DeleteObjectAclRequest deleteObjectAclRequest = new DeleteObjectAclRequest("yourBucketName","objectKey");
client.deleteObjectAcl(deleteObjectAclRequest);
删除文件
删除单个文件
可参考如下代码删除了一个Object:
java
public void deleteObject(FosClient client, String bucketName, String objectKey) {
// 删除Object
client.deleteObject(<bucketName>, <objectKey>); //指定要删除的Object所在Bucket名称和该Object名称
}
删除多个文件
可参考如下两种方法删除多个Object:
java
// 1、以Json格式的字符串
String jsonObjectKeys = "{\"objects\": ["+"{\"key\": \"token1.h\"},"+"{\"key\": \"token2.h\"}"+"]}";
DeleteMultipleObjectsRequest request = new DeleteMultipleObjectsRequest();
request.setBucketName("yourBucketName");
request.setJsonDeleteObjects(jsonObjectKeys);
client.deleteMultipleObjects(request);
java
// 2、用户只需指定指定参数即可
List<String> objectKeys = new ArrayList<String>();
objectKeys.add("object1");
objectKeys.add("object2");
DeleteMultipleObjectsRequest request = new DeleteMultipleObjectsRequest();
request.setBucketName("yourBucketName");
request.setObjectKeys(objectKeys);
DeleteMultipleObjectsResponse response = client.deleteMultipleObjects(request);
支持一次请求内最多删除1000个Object。 消息体(body)不超过2M。 返回的消息体中只包含删除过程中出错的Object结果;如果所有Object都删除都成功的话,则没有消息体。
查看文件是否存在
用户可通过如下操作查看某文件是否存在:
java
// 存在返回true,不存在返回false,其他异常会被抛出
bool exists = client.doesObjectExist("bucketName", "objectKey");
获取及更新文件元信息
文件元信息(Object Metadata),是对用户上传FOS的文件的属性描述,分为两种:HTTP标准属性(HTTP Headers)和User Meta(用户自定义元信息)。
获取文件元信息
通过 getObjectMetadata 方法可以只获取ObjectMetadata而不获取Object的实体。 如下代码所示:
java
ObjectMetadata objectMetadata = client.getObjectMetadata(bucketName, objectKey);
// 查看object元信息
System.out.println("contentType: " + objectMetadata.getContentType() + "\n" +
"contentLength: " + objectMetadata.getContentLength() + "\n" +
"contentMd5: " + objectMetadata.getContentMd5() + "\n" +
"etag: " + objectMetadata.getETag() + "\n" +
"storageClass: " + objectMetadata.getStorageClass() + "\n");
getObjectMetadata方法返回的解析类中可供调用的参数有:
参数 | 说明 |
---|---|
contentType | Object的类型 |
contentLength | Object的大小 |
contentMd5 | Object的MD5 |
etag | Object的HTTP协议实体标签 |
storageClass | Object的存储类型 |
userMetadata | 如果在PutObject指定了userMetadata自定义meta,则返回此项 |
xIcCrc | 如果在PutObject指定了object的CRC值(循环冗余校验码),则返回此项 |
修改文件元信息
FOS修改Object的Metadata通过拷贝Object实现。即拷贝Object的时候,把目的Bucket设置为源Bucket,目的Object设置为源Object,并设置新的Metadata,通过拷贝自身实现修改Metadata的目的。如果不设置新的Metadata,则报错。
java
public void setObjectMeta(FosClient client, String bucketName, String objectKey, ObjectMetadata newObjectMetadata) {
CopyObjectRequest request = new CopyObjectRequest(bucketName, objectKey, bucketName, objectKey);
// 设置新的ObjectMetadata
request.setNewObjectMetadata(newObjectMetadata);
// 拷贝Object
CopyObjectResponse copyObjectResponse = client.copyObject(request);
// 打印结果
System.out.println("ETag: " + copyObjectResponse.getETag() + " LastModified: " + copyObjectResponse.getLastModified());
}
拷贝文件
拷贝一个文件
用户可以通过copyObject方法拷贝一个Object,如下代码所示:
java
public void copyObject(FosClient client, String srcBucketName, String srcKey, String destBucketName, String destKey) {
// 拷贝Object
CopyObjectResponse copyObjectResponse = client.copyObject(srcBucketName, srcKey, destBucketName, destKey);
// 打印结果
System.out.println("ETag: " + copyObjectResponse.getETag() + " LastModified: " + copyObjectResponse.getLastModified());
}
copyObject 方法返回一个 CopyObjectResponse 对象,该对象中包含了新Object的ETag和修改时间。
通过CopyObjectRequest拷贝Object
用户也可以通过 CopyObjectRequest 实现Object的拷贝,如下代码所示:
java
// 初始化FosClient
FosClient client = ...;
// 创建CopyObjectRequest对象
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketName, srcKey, destBucketName, destKey);
// 设置新的Metadata
Map<String, String> userMetadata = new HashMap<String, String>();
userMetadata.put('<user-meta-key>','<user-meta-value>');
ObjectMetadata meta = new ObjectMetadata();
meta.setUserMetadata(userMetadata);
copyObjectRequest.setNewObjectMetadata(meta);
// 复制Object
CopyObjectResponse copyObjectResponse = client.copyObject(copyObjectRequest);
System.out.println("ETag: " + copyObjectResponse.getETag() + " LastModified: " + copyObjectResponse.getLastModified());
CopyObjectRequest 允许用户修改目的Object的ObjectMeta,同时也提供 MatchingETagConstraints 参数的设定。 设置Object的Copy属性 FOS同时会提供CopyObject接口用于将一个已经存在的Object拷贝到另外一个Object,拷贝过程中会对源Object的Etag或修改状态进行判断,根据判断结果决定是否执行拷贝。详细的参数解释如下:
名称 | 类型 | 描述 | 是否必需 |
---|---|---|---|
x-ic-copy-source-if-match | String | 如果源Object的ETag值和用户提供的ETag相等,则执行拷贝操作,否则拷贝失败。 | 否 |
x-ic-copy-source-if-none-match | String | 如果源Object的ETag和用户提供的ETag不相等,则执行拷贝操作,否则拷贝失败。 | 否 |
x-ic-copy-source-if-unmodified-since | String | 如果源object在x-ic-copy-source-if-unmodified-since之后没被修改,则执行拷贝操作,否则拷贝失败。 | 否 |
x-ic-copy-source-if-modified-since | String | 如果源object在x-ic-copy-source-if-modified-since之后被修改了,则执行拷贝操作,否则拷贝失败。 | 否 |
对应的示例代码:
java
// 初始化FosClient
FosClient client = ...;
// 创建CopyObjectRequest对象
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(srcBucketName, srcKey, destBucketName, destKey);
// 设置新的Metadata
Map<String, String> userMetadata = new HashMap<String, String>();
userMetadata.put("<user-meta-key>","<user-meta-value>");
meta.setUserMetadata(userMetadata);
copyObjectRequest.setNewObjectMetadata(meta);
//copy-source-if-match
copyObjectRequest.withETag("111111111183bf192b57a4afc76fa632");
//copy-source-if-none-match
copyObjectRequest.withNoMatchingETagConstraint("111111111183bf192b57a4afc76fa632");
Date modifiedSinceConstraint = new Date();
SimpleDateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.UK);
df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
String date = df.format(modifiedSinceConstraint);
//copy-source-if-modified-since
copyObjectRequest.withModifiedSinceConstraint(date);
//copy-source-if-unmodified-since
copyObjectRequest.withUnmodifiedSinceConstraint(date);
// 复制Object
CopyObjectResponse copyObjectResponse = client.copyObject(copyObjectRequest);
System.out.println("ETag: " + copyObjectResponse.getETag() + " LastModified: " + copyObjectResponse.getLastModified());
同步Copy功能 当前FOS的CopyObject接口是通过同步方式实现的。同步方式下,FOS端会等待Copy实际完成才返回成功。同步Copy能帮助用户更准确的判断Copy状态,但用户感知的复制时间会变长,且复制时间和文件大小成正比。 同步Copy方式更符合业界常规,提升了与其它平台的兼容性。同步Copy方式还简化了FOS服务端的业务逻辑,提高了服务效率。
分块拷贝
除了通过CopyObject接⼝拷贝文件以外,FOS还提供了另外一种拷贝模式——Multipart Upload Copy。用户可以在如下的应用场景内(但不仅限于此),使用Multipart Upload Copy,如:
- 需要支持断点拷贝。
- 拷贝超过5GB大小的文件。
- 网络条件较差,和FOS的服务器之间的连接经常断开。
下面将介绍分步实现三步拷贝。 三步拷贝包含init、“拷贝分块”和complete三步,其中init和complete的操作同分块上传一致。 为了便于理解,下面提供三步拷贝完整代码:
java
// 第一步 init
InitiateMultipartUploadRequest initiateMultipartUploadRequest =
new InitiateMultipartUploadRequest("targetBucketName","targetObjectName");
InitiateMultipartUploadResponse initiateMultipartUploadResponse =
client.initiateMultipartUpload(initiateMultipartUploadRequest);
// 第二步 分块拷贝
long left_size=client.getObjectMetadata("sourceBucketName","sourceObjectName").getContentLength();
long skipBytes = 0;
int partNumber = 1;
List<PartETag> partETags = new ArrayList<PartETag>();
while (left_size > 0) {
long partSize = 1024 * 1024 * 1L;
if (left_size < partSize) {
partSize = left_size;
}
UploadPartCopyRequest uploadPartCopyRequest = new UploadPartCopyRequest();
uploadPartCopyRequest.setBucketName("targetBucketName");
uploadPartCopyRequest.setKey("targetObjectName");
uploadPartCopyRequest.setSourceBucketName("sourceBucketName");
uploadPartCopyRequest.setSourceKey("sourceObjectName");
uploadPartCopyRequest.setUploadId(initiateMultipartUploadResponse.getUploadId());
uploadPartCopyRequest.setPartSize(partSize);
uploadPartCopyRequest.setOffSet(skipBytes);
uploadPartCopyRequest.setPartNumber(partNumber);
UploadPartCopyResponse uploadPartCopyResponse = client.uploadPartCopy(uploadPartCopyRequest);
// 将返回的PartETag保存到List中
PartETag partETag = new PartETag(partNumber,uploadPartCopyResponse.getETag());
partETags.add(partETag);
left_size -= partSize;
skipBytes += partSize;
partNumber+=1;
}
// 第三步 complete
CompleteMultipartUploadRequest completeMultipartUploadRequest =
new CompleteMultipartUploadRequest("targetBucketName", "targetObjectName", initiateMultipartUploadResponse.getUploadId(), partETags);
CompleteMultipartUploadResponse completeMultipartUploadResponse =
client.completeMultipartUpload(completeMultipartUploadRequest);
注意:
- offset参数以字节为单位,为分块的开始偏移位置。
- size参数以字节为单位,定义每个分块的大小,除最后一个Part以外,其他的Part大小都要大于 5MB。
数据处理及使用
生命周期管理
FOS支持用户对Bucket设置生命周期规则,以自动将过期的文件清除,节省存储空间。针对不同前缀的文件,用户可以同时设置多条规则。
设置生命周期规则
可以通过如下两种方式设置生命周期规则:
java
// 1、以Json格式的字符串
String jsonBucketLifecylce = "{\"rule\":[{\"id\":\"yourBucketName/*-DeleteObject\""+",\"status\":\"enabled\""+",\"resource\":[\"yourBucketName/*\"]"+",\"condition\":{\"time\":{\"dateGreaterThan\":\"2018-09-07T00:00:00Z\"}}"+",\"action\":{\"name\":\"DeleteObject\"}}]}";
SetBucketLifecycleRequest request = new SetBucketLifecycleRequest("yourBucketName",jsonBucketLifecylce);
client.setBucketBucketLifecycle(request);
java
// 2、用户只需指定指定参数即可
List<Rule> rules = new ArrayList<Rule>();
List<String> resource = new ArrayList<String>();
Action action = new Action();
Condition condition = new Condition();
resource.add("yourBucketName/*");
action.setName("DeleteObject");
Time time = new Time();
time.setDateGreaterThan("2018-09-07T00:00:00Z");
condition.setTime(time);
Rule rule = new Rule();
rule.setId("yourBucketName/*-DeleteObject");
rule.setStatus("enabled");
rule.setResource(resource);
rule.setCondition(condition);
rule.setAction(action);
List<String> resource1 = new ArrayList<String>();
Action action1 = new Action();
Condition condition1 = new Condition();
resource1.add("yourBucketName/*");
action1.setName("Transition");
action1.setStorageClass("STANDARD_IA");
Time time1 = new Time();
time1.setDateGreaterThan("2018-09-08T00:00:00Z");
condition1.setTime(time1);
Rule rule1 = new Rule();
rule1.setId("yourBucketName/*-Transition");
rule1.setStatus("enabled");
rule1.setResource(resource1);
rule1.setCondition(condition1);
rule1.setAction(action1);
rules.add(rule1);
rules.add(rule);
SetBucketLifecycleRequest request = new SetBucketLifecycleRequest();
request.withBucketName("yourBucketName");
request.setRuleList(rules);
client.setBucketBucketLifecycle(request);
查看生命周期规则
可通过如下代码查看Bucket内的生命周期规则:
java
GetBucketLifecycleRequest request = new GetBucketLifecycleRequest("yourBucketName");
GetBucketLifecycleResponse response = client.getBucketLifecycle(request);
删除生命周期规则
可通过如下代码清空生命周期规则:
java
DeleteBucketLifecycleRequest request = new DeleteBucketLifecycleRequest("yourBucketName");
client.deleteBucketLifecycle(request);
跨域资源共享
跨域资源共享(Cross-Origin Resource Sharing),简称CORS,是HTML5提供的标准跨域解决方案,FOS目前已经支持CORS标准来实现跨域访问。
设定CORS规则
可以通过如下两种方式设置跨域规则:
java
// 1、以Json格式的字符串
String jsonBucketCors = "{\"corsConfiguration\": [{\"allowedOrigins\":[\"http://*\""+",\"https://*\"]"+",\"allowedMethods\":[\"GET\""+",\"HEAD\""+",\"POST\""+",\"PUT\"]"+",\"allowedHeaders\":[\"*\"]"+",\"allowedExposeHeaders\":[\"ETag\""+",\"Content-Length\""+",\"x-ic-next-append-offset\""+",\"x-ic-object-type\""+",\"x-ic-request-id\"]"+",\"maxAgeSeconds\":1800}]}";
SetBucketCorsRequest request = new SetBucketCorsRequest();
request.setJsonBucketCors(jsonBucketCors);
request.withBucketName("yourBucketName");
client.setBucketBucketCors(request);
java
// 2、用户只需指定指定参数即可
SetBucketCorsRequest request = new SetBucketCorsRequest();
request.setBucketName("yourBucketName");
List<CorsConfiguration> corsConfigurations = new ArrayList<CorsConfiguration>();
List<String> allowedOrigins = new ArrayList<String>();
List<AllowedMethods> allowedMethods = new ArrayList<AllowedMethods>();
List<String> allowedHeaders = new ArrayList<String>();
List<String> allowedExposeHeaders = new ArrayList<String>();
CorsConfiguration corsConfiguration = new CorsConfiguration();
CorsConfiguration corsConfiguration1 = new CorsConfiguration();
// AllowedOrigins
allowedOrigins.add("http://www.example.com");
allowedOrigins.add("http://www.example1.com");
allowedMethods.add(AllowedMethods.GET);
allowedMethods.add(AllowedMethods.POST);
allowedMethods.add(AllowedMethods.PUT);
allowedMethods.add(AllowedMethods.DELETE);
// allowedHeaders
allowedHeaders.add("head1");
allowedHeaders.add("head2");
// allowedExposeHeaders
allowedExposeHeaders.add("ETag");
allowedExposeHeaders.add("Content-Length");
allowedExposeHeaders.add("x-ic-next-append-offset");
allowedExposeHeaders.add("x-ic-object-type");
corsConfiguration.setAllowedOrigins(allowedOrigins);
corsConfiguration.setAllowedMethods(allowedMethods);
corsConfiguration.setAllowedHeaders(allowedHeaders);
corsConfiguration.setAllowedExposeHeaders(allowedExposeHeaders);
corsConfiguration.setMaxAgeSeconds(1800);
List<String> allowedOrigins1 = new ArrayList<String>();
List<AllowedMethods> allowedMethods1 = new ArrayList<AllowedMethods>();
List<String> allowedHeaders1 = new ArrayList<String>();
List<String> allowedExposeHeaders1 = new ArrayList<String>();
corsConfigurations.add(corsConfiguration);
// AllowedOrigins
allowedOrigins1.add("allowedOrigin1");
allowedOrigins1.add("allowedOrigin2");
// allowedMethods
allowedMethods1.add(AllowedMethods.GET);
allowedMethods1.add(AllowedMethods.POST);
allowedMethods1.add(AllowedMethods.PUT);
allowedMethods1.add(AllowedMethods.DELETE);
// allowedHeaders
allowedHeaders1.add("head3");
allowedHeaders1.add("head4");
// allowedExposeHeaders
allowedExposeHeaders1.add("ETag");
allowedExposeHeaders1.add("Content-Length");
allowedExposeHeaders1.add("x-ic-next-append-offset");
allowedExposeHeaders1.add("x-ic-object-type");
corsConfiguration1.setAllowedOrigins(allowedOrigins1);
corsConfiguration1.setAllowedMethods(allowedMethods1);
corsConfiguration1.setAllowedHeaders(allowedHeaders1);
corsConfiguration1.setAllowedExposeHeaders(allowedExposeHeaders1);
corsConfiguration1.setMaxAgeSeconds(3600);
corsConfigurations.add(corsConfiguration1);
request.setCorsConfigurationsList(corsConfigurations);
client.setBucketBucketCors(request);
注意:
- 通过此接口设置CORS规则之前,Bucket的CORS权限设置为不允许跨域。
- 每个Bucket最多只允许有一个规则文件,因此新上传的规则文件会覆盖原有的。
- CORS规则文件大小限制为20KB,因此请求时大于20KB会返回超出大小错误(400 Bad Request: EntityTooLarge)。
获取CORS规则
可以通过如下方式获取跨域规则:
java
GetBucketCorsRequest request = new GetBucketCorsRequest();
request.setBucketName("yourBucketName");
GetBucketCorsResponse response = client.getBucketCros(request);
删除CORS规则
可以通过如下方式删除跨域规则:
java
DeleteBucketCorsRequest request = new DeleteBucketCorsRequest("yourBucketName");
client.deleteBucketCors(request);
异常处理
FOS异常提示有如下两种方式:
异常方法 | 说明 |
---|---|
FceClientException | 客户端异常 |
FceServiceException | 服务器异常 |
用户可以使用try获取某个事件所产生的异常,例如:
java
String objectKey = "testObjectToFile";
FosClient.putObject(bucketName, objectKey, "dataFile");
File file = new File("test");
try {
FosClient.getObject(this.bucketName, objectKey, file);
} catch (FceServiceException fce){
System.out.println(fce.getMessage());
} catch ( FceClientException fce){
System.out.println( fce.getMessage());
} finally {
file.delete();
}
客户端异常
客户端异常表示客户端尝试向FOS发送请求以及数据传输时遇到的异常。 例如,当发送请求时网络连接不可用时,则会抛出 ClientException;当上传文件时发生IO异常时,也会抛出ClientException。
服务端异常
当FOS服务端出现异常时,FOS服务端会返回给用户相应的错误信息,以便定位问题。
SDK日志
FOS Java SDK发布版本中增加了logback作为slf4j的实现,如果用户没有自己的实现可以直接用,如果工程中有其他的如log4j则可以替代。
默认日志
如果用户使用默认的logback,则需要配置logback.xml到classpath中。如果没有这个配置文件,日志级别默认为DEBUG。
xml
<configuration>
<property name="LOG_HOME" value="./log/"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/FosUnitTest.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
自有日志模块
若用户使用自己的日志实现模块,例如项目依赖于Maven,则可以类似下面的配置到pom.xml中来去除logback。
xml
<?xml version="1.0" encoding="utf-8"?>
<dependency>
<groupId>com.flymeyun</groupId>
<artifactId>ic-java-sdk</artifactId>
<version>${ic.sdk.version}</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>