最近更新时间:2021-12-16 11:41:44
KS3 SDK For Java使用指南
文档中的示例代码仅供参考之用,具体使用的时候请参考KS3 API文档,根据自己的实际情况调节参数。
lib目录下为该项目所依赖的所有jar包,以及将sdk打好的jar包
此SDK适用于Java 5及以上版本。基于KS3 API 构建。
配置Java 5 以上开发环境
添加Maven依赖
<dependency>
<groupId>com.ksyun</groupId>
<artifactId>ks3-kss-java-sdk</artifactId>
<version>1.0.2</version>
</dependency>
或者直接引用lib目录下的所有jar包
Ks3ClientConfig config = new Ks3ClientConfig();
/**
* 设置服务地址
* 中国(北京)| ks3-cn-beijing.ksyuncs.com
* 中国(上海)| ks3-cn-shanghai.ksyuncs.com
* 中国(香港)| ks3-cn-hk-1.ksyuncs.com
* 中国(广州)| ks3-cn-guangzhou.ksyuncs.com
* 中国(青岛)| ks3-cn-qingdao.ksyuncs.com
* 金融专区(北京)| ks3-jr-beijing.ksyuncs.com
* 金融专区(上海)| ks3-jr-shanghai.ksyuncs.com
* 俄罗斯 | ks3-rus.ksyuncs.com
* 新加坡 | ks3-sgp.ksyuncs.com
* 如果使用自定义域名,设置endpoint为自定义域名,同时设置domainMode为true
*/
config.setEndpoint("ks3-cn-beijing.ksyuncs.com"); //此处以北京region为例
/**
*true:表示以自定义域名访问
*false:表示以KS3的外网域名或内网域名访问,默认为false
*/
config.setDomainMode(false);
config.setProtocol(PROTOCOL.http);
/**
*true表示以 endpoint/{bucket}/{key}的方式访问
*false表示以 {bucket}.endpoint/{key}的方式访问
*如果domainMode设置为true,pathStyleAccess可忽略设置
*/
config.setPathStyleAccess(false);
HttpClientConfig hconfig = new HttpClientConfig();
//在HttpClientConfig中可以设置httpclient的相关属性,比如代理,超时,重试等。
config.setHttpClientConfig(hconfig);
Ks3 client = new Ks3Client("<您的AccessKeyID>","<您的AccessKeySecret>",config);
/* 或者:client.setKs3config(config); */
该SDK使用commons-logging
使用log4j的示例:
引用log4j相关jar包
新建log4j.properties(如下为示例配置)
log4j.logger.com.ksyun.ks3=DEBUG,stdout
log4j.logger.org.apache.http=DEBUG,stdout
log4j.logger.org.apache.http.wire=ERROR,stdout
log4j.addivity.org.apache=true
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss SSS} %-5p [%t]%C{1}.%M(%L) - %m%n
使用logback示例
引用SDK的时候排除commons-logging,引用logback相关包(包括但不止jcl-over-slf4j)。
配置logback
开通KS3服务,https://www.ksyun.com/user/register 注册账号
当以上全部完成之后用户便可初始化客户端进行操作了
Ks3 client = new Ks3Client("<您的AccessKeyID>","<您的AccessKeySecret>");
使用自定义的配置(3.1)
Ks3 client = new Ks3Client("<您的AccessKeyID>","<您的AccessKeySecret>",config);
或者:client.setKs3config(config);
在KS3中,用户操作的基本数据单元是Object。单个Object允许存储0~48.8TB的数据。 Object 包含key和data。其中,key是Object的名字;data是Object 的数据。key为UTF-8编码,且编码后的长度不得超过1024个字符。
即Object的名字,key为UTF-8编码,且编码后的长度不得超过1024个字符。Key中可以带有斜杠,当Key中带有斜杠的时候,将会自动在控制台里组织成目录结构。
其他术语请参考概念与术语
请先阅读常用概念术语文档。
进入控制台,点击右上角新建空间,新建的空间名称即为之后提到的bucket。
/**
*将new File("<filePath>")这个文件上传至<bucket名称>这个存储空间下,并命名为<key>
*/
public void putObjectSimple(){
PutObjectRequest request = new PutObjectRequest("<bucket名称>",
"<key>", new File("<filePath>"));
//上传一个公开文件
//request.setCannedAcl(CannedAccessControlList.PublicRead);
client.putObject(request);
}
说明:
- 该方法需要三个参数,第一个参数为在4.1时新建的存储空间名称。第二个参数为文件在KS3上保持的路径,比如:image.jpg,2015/10/19/image.jpg 等。第三个参数为要上传的文件对象。
了解KS3表单上传协议,以及表单上传签名认证方式。
在KS3控制台->空间设置->CORS配置里配置CORS跨域规则。CORS为跨域资源共享,当使用js跨域时,需要配置该规则。W3C文档。
js sdk 中的policy和signature可以通过Java SDK的该方法计算出来。
/**
如果用户对KS3协议不是特别清楚,建议使用该方法。每次上传的时候都去获取一次最新的签名信息
*/
public PostObjectFormFields postObjectSimple(){
/**
* 需要用户在postData和unknowValueField中提供所有的除KSSAccessKeyId, signature, file, policy外的所有表单项。否则用生成的签名上传会返回403</br>
* 对于用户可以确定表单值的放在 postData中,对于用户无法确定表单值的放在unknownValueField中(比如有的上传控件会添加一些表单项,但表单项的值可能是随机的)</br>
*
*/
Map<String,String> postData = new HashMap<String,String>();、
//如果使用js sdk上传的时候设置了ACL,请提供以下一行,且值要与SDK中一致,否则删除下面一行代码
postData.put("acl","public-read");
//提供js sdk中的key值
postData.put("key","20150115/中文/${filename}");
List<String> unknowValueField = new ArrayList<String>();
//js sdk上传的时候会自动加上一个name的表单项,所以下面需要加上这样的代码。
unknowValueField.add("name");
//如果计算签名时提供的key里不包含${filename}占位符,可以把第二个参数传一个空字符串。因为计算policy需要的key是把${filename}进行替换后的key。
PostObjectFormFields fields = client.postObject("<您的bucket名称>", "<要上传的文件名称,不包含路径信息>", postData, unknowValueField);
fields.getKssAccessKeyId();
fields.getPolicy();
fields.getSignature();
return fields;
}
常见问题:
- 上传时,浏览器一共会发送两个请求,第一个为OPTIONS请求,第二个为POST请求。如果第一个请求返回403,请检测CORS配置是否正确。如果第二个请求返回403,请检查生成policy时,是否完全按照表单内容生成。或者把policy进行base64解码,对比表单内容是否正确。对比规则:Policy、Signature构建方法。
通过:http://{bucket}.{endpoint}/{key}
的方式拼接一个URL即可。比如:http://test-bucket.ks3-cn-beijing.ksyuncs.com/2015/10/19/image.jpg
,该URL中的{bucket}是test-bucket,{endpoint}是kssws.ks-cdn.com,{key}是2015/10/19/image.jpg。
通过以下代码可以生成一个访问地址
//生成一个在1000秒后过期的外链
client.generatePresignedUrl(<bucket>,<key>,1000);
//生成一个1000秒后过期并重写返回的heade的外链
ResponseHeaderOverrides overrides = new ResponseHeaderOverrides();
//overrides.setContentType("text/html");
//.......
client.generatePresignedUrl(<bucket>,<key>,1000,overrides);
常见问题:
- 如果文件不存在,会返回NoSuchKey错误。
- 如果以公开的方式访问私有文件,会返回AccessDined错误。
- 如果私有文件访问地址过期,会返回URLExpired错误。
- 1000秒后过期是参照客户端本地时间的。
通过该接口,用户可以获取到其所有的bucket。
public List<Bucket> listBuckets(){
List<Bucket> buckets = client.listBuckets();
for(Bucket bucket:buckets){
//获取bucket的创建时间
bucket.getCreationDate();
//获取bucket的名称
bucket.getName();
//获取bucket的拥有者(用户ID base64后的值)
bucket.getOwner();
//获取bucket的区域信息
bucket.getRegion();
//获取bucket类型,非归档(BucketType.Normal)、归档(BucketType.Archive)
bucket.getType();
}
return buckets;
}
/**
* 使用最简单的方式创建一个bucket
* 将使用默认的配置,权限为私有,存储地点为北京
*/
public void createBucketSimple(){
client.createBucket("<您的bucket名称>");
}
/**
* 新建bucket的时候配置bucket的存储地点和访问权限
*/
public void createBucketWithConfig(){
CreateBucketRequest request = new CreateBucketRequest("<您的bucket名称>");
//配置bucket的存储地点
CreateBucketConfiguration config = new CreateBucketConfiguration(REGION.BEIJING);
request.setConfig(config);
//配置bucket类型,非归档(BucketType.Normal)、归档(BucketType.Archive)。默认为非归档。
request.setBucketType(BucketType.Normal);
//配置bucket的访问权限
request.setCannedAcl(CannedAccessControlList.Private);
//执行操作
client.createBucket(request);
}
注意:
- bucket名称是全局唯一的。
bucket的访问权限说明可参照:权限概述。
/**
*通过预设的ACL设置bucket的访问权限,预设的ACL包括:private:私有。public-read:为所有用户授予read权限。public-read-write:为所有用户授予read和write权限,
*/
public void putBucketAclWithCannedAcl(){
PutBucketACLRequest request = new PutBucketACLRequest("<您的bucket名称>");
//设为私有
request.setCannedAcl(CannedAccessControlList.Private);
//设为公开读
//request.setCannedAcl(CannedAccessControlList.PublicRead);
//设为公开读写
//request.setCannedAcl(CannedAccessControlList.PublicReadWrite);
client.putBucketACL(request);
}
/**
*自定义bucket的访问权限。
*/
public void putBucketAclWithAcl(){
PutBucketACLRequest request = new PutBucketACLRequest("<您的bucket名称>");
AccessControlList acl = new AccessControlList();
//设置用户id为12345678的用户对bucket的读权限
Grant grant1 = new Grant();
grant1.setGrantee(new GranteeId("12345678"));
grant1.setPermission(Permission.Read);
acl.addGrant(grant1);
//设置用户id为123456789的用户对bucket完全控制
Grant grant2 = new Grant();
grant2.setGrantee(new GranteeId("123456789"));
grant2.setPermission(Permission.FullControl);
acl.addGrant(grant2);
//设置用户id为12345678910的用户对bucket的写权限
Grant grant3 = new Grant();
grant3.setGrantee(new GranteeId("12345678910"));
grant3.setPermission(Permission.Write);
acl.addGrant(grant3);
request.setAccessControlList(acl);
client.putBucketACL(request);
}
public AccessControlPolicy getBucketAcl(){
AccessControlPolicy acl = null;
//只需要传入这个bucket的名称即可
acl = client.getBucketACL("<您的bucket名称>");
return acl;
}
说明:
- AccessControlPolicy中有public CannedAccessControlList 。getCannedAccessControlList()方法可以从ACL中提取出预设ACL来。
设置bucket的日志配置,当为bucket添加上该配置后,KS3将自动把bucket及bucket下的文件的操作日志放到指定的空间中
/**
* 将存储空间的操作日志存储在 <存放日志文件的bucket名称> 里面,日志文件的前缀是"logging-"
*/
public void putBucketLogging(){
PutBucketLoggingRequest request = new PutBucketLoggingRequest("<您的bucket名称>");
//开启日志
request.setEnable(true);
request.setTargetBucket("<存放日志文件的bucket名称>");
//设置日志文件的前缀为logging-
request.setTargetPrefix("logging-");
client.putBucketLogging(request);
}
注意:
- 存放日志文件的bucket必须是当前用户同一个region的bucket。
- 要配置日志的bucket和存放日志文件的bucket可以是一个bucket,但是为了用户方便管理,建议使用两个不同的bucket。
5.6 获取bucket的日志配置
public BucketLoggingStatus getBucketLogging(){
//只需要传入bucket的名称,由于ks3暂时对日志权限不支持,所以返回的BucketLoggingStatus中targetGrants始终为空集合
BucketLoggingStatus logging = client.getBucketLogging("<您的bucket名称>");
return logging;
}
/**
* Head请求一个bucket
*/
public HeadBucketResult headBucket() {
HeadBucketResult result = client.headBucket("<您的bucket名称>");
/**
* 通过result.getStatueCode()状态码 404则这个bucket不存在,403当前用户没有权限访问这个bucket
*/
return result;
}
public REGION getBucketLocation(){
//只需要传入bucket的名称
REGION region = client.getBucketLoaction("<您的bucket名称>");
return region;
}
public void putBucketCors(){
BucketCorsConfiguration config = new BucketCorsConfiguration();
//资源跨域共享规则
CorsRule rule1 = new CorsRule();
//指定允许的跨域请求方法(GET/PUT/DELETE/POST/HEAD)
List<AllowedMethods> allowedMethods = new ArrayList<AllowedMethods>();
allowedMethods.add(AllowedMethods.GET);
//指定允许跨域请求的来源
List<String> allowedOrigins = new ArrayList<String>();
allowedOrigins.add("http://example.com");
//指定允许用户从应用程序中访问的响应头
List<String> exposedHeaders = new ArrayList<String>();
exposedHeaders.add("x-kss-test1");
//控制在 OPTIONS 预取指令中 Access-Control-Request-Headers 头中指定的 header 是否允许。
List<String> allowedHeaders = new ArrayList<String>();
allowedHeaders.add("x-kss-test");
//将允许的跨域请求header添加到规则中
rule1.setAllowedHeaders(allowedHeaders);
//将允许的跨域请求方法添加到规则中
rule1.setAllowedMethods(allowedMethods);
//将允许跨域请求的来源添加到规则中
rule1.setAllowedOrigins(allowedOrigins);
//将允许用户从应用程序中访问的响应头添加到规则中
rule1.setExposedHeaders(exposedHeaders);
//指定浏览器对特定资源的预取(OPTIONS)请求返回结果的缓存时间,单位为秒。
rule1.setMaxAgeSeconds(200);
config.addRule(rule1);
//设置另一条规则,一个bucket可以最多配置10条规则
CorsRule rule2 = new CorsRule();
//指定允许的跨域请求方法为 (支持GET/PUT/DELETE/POST/HEAD)
List<AllowedMethods> allowedMethods2 = new ArrayList<AllowedMethods>();
//增加允许的跨域请求方法为GET
allowedMethods2.add(AllowedMethods.GET);
//增加允许的跨域请求方法为POST
allowedMethods2.add(AllowedMethods.POST);
//设置两个允许跨域请求的来源 ,支持通配符星号(*)
List<String> allowedOrigins2 = new ArrayList<String>();
allowedOrigins2.add("http://example.com");
allowedOrigins2.add("http://*.example.com");
//设置两个允许用户从应用程序中访问的响应头
List<String> exposedHeaders2 = new ArrayList<String>();
exposedHeaders2.add("x-kss-test1");
exposedHeaders2.add("x-kss-test2");
//控制在 OPTIONS 预取指令中 Access-Control-Request-Headers 头中指定的 header 是否允许。
List<String> allowedHeaders2 = new ArrayList<String>();
allowedHeaders2.add("x-kss-test");
allowedHeaders2.add("x-kss-test2");
//将允许的跨域请求header添加到规则中
rule2.setAllowedHeaders(allowedHeaders2);
//将允许的跨域请求方法添加到规则中
rule2.setAllowedMethods(allowedMethods2);
//将允许跨域请求的来源添加到规则中
rule2.setAllowedOrigins(allowedOrigins2);
//将允许用户从应用程序中访问的响应头添加到规则中
rule2.setExposedHeaders(exposedHeaders2);
//指定浏览器对特定资源的预取(OPTIONS)请求返回结果的缓存时间,单位为秒。
rule2.setMaxAgeSeconds(500);
config.addRule(rule2);
PutBucketCorsRequest request = new PutBucketCorsRequest("<您的bucket名称>",config);
client.putBucketCors(request);
}
注意:
- CORS是指跨域资源共享,当使用javascript进行跨域的时候,需要为bucket配置该规则。W3C文档https://www.w3.org/TR/cors/。
- AllowedMethods、AllowedHeaders和AllowedOrigins为必选参数。AllowedHeaders和AllowedOrigins中支持最多一个*号的通配。
public BucketCorsConfiguration getBucketCors(){
BucketCorsConfiguration config = client.getBucketCors("test.bucket");
List<CorsRule> rules = config.getRules();
for(CorsRule rule : rules){
//控制在 OPTIONS 预取指令中 Access-Control-Request-Headers 头中指定的 header 是否允许。
rule.getAllowedHeaders();
//允许的跨域请求方法(GET/PUT/DELETE/POST/HEAD)
rule.getAllowedMethods();
//允许跨域请求的来源
rule.getAllowedOrigins();
//允许用户从应用程序中访问的响应头
rule.getExposedHeaders();
//浏览器对特定资源的预取(OPTIONS)请求返回结果的缓存时间,单位为秒。
rule.getMaxAgeSeconds();
}
return config;
}
public void deleteBucketCors(){
client.deleteBucketCors("test.bucket");
}
public void deleteBucket(){
client.deleteBucket("test-bucket");
}
以下代码用于开启复制规则:
/**
* 在源存储空间上开启复制
*/
//初始化复制规则对象
ReplicationRule rule = new ReplicationRule();
//设置前缀,只有前缀匹配到的文件会被同步
rule.getPrefixList().add(prefix1);//不添加默认同步全部文件
rule.getPrefixList().add(prefix2);//最多可以添加10条前缀规则,且前缀之间不能重叠
//是否开始删除同步,若显式指定为true为开启,若为false或不指定均为关闭状态
rule.setDeleteMarkerStatus(Enabled);
rule.setTargetBucket(bucketName);//选择目标桶
//在源桶上创建复制规则
client.putBucketReplicationConfiguration(bucketName,rule);
/**
* 查看存储空间上的复制规则,如存储空间上没有开通复制规则,则返回404
*/
ReplicationRule replicationRule = client.getBucketReplicationConfiguration(bucketName);
/**
* 关闭存储空间上的复制,如存储空间上没有开通或已关闭复制规则,则返回404
*/
ReplicationRule replicationRule = client.deleteBucketReplicationConfiguration(bucketName);
//初始化回源规则对象,依次设置回源规则版本(目前只支持V3)、是否使用默认的robots.txt、异步回源规则、同步回源规则
BucketMirror bucketMirror = new BucketMirror(BucketMirror.Version.V3, false, new BucketMirror.AsyncMirrorRule(
Arrays.asList("http://www.example1.com", "https://www.example2.com"), null), null);
//在目标桶上创建回源规则
client.putBucketMirror("bucketName", bucketMirror);
/*5.16.2、新增或者更新同步回源规则*/
//设置回源规则列表,最多可配置20个回源规则
List<BucketMirror.SyncMirrorRule> syncMirrorRules = new ArrayList<>();
//初始化同步回源规则对象
BucketMirror.SyncMirrorRule smr1 = new BucketMirror.SyncMirrorRule();
//设置源站url,必须以http或者https开头,域名部分最多不超过256个字符,path部分最多不超过1024个字符
smr1.setMirrorUrl("https://www.example1.com");
//将规则加入规则列表
syncMirrorRules.add(smr1);
//设置另一条同步回源规则
BucketMirror.SyncMirrorRule smr2 = new BucketMirror.SyncMirrorRule();
smr2.setMirrorUrl("https://www.example1.com");
syncMirrorRules.add(smr2);
//新增同步回源规则
BucketMirror bucketMirror = new BucketMirror(BucketMirror.Version.V3, false, null, syncMirrorRules);
client.putBucketMirror("bucketName", bucketMirror);
/*5.16.3、新增或者更新异步回源规则,并设置回源文件的acl为PublicRead*/
//初始化回源规则对象,并设置异步回源规则的源站url(数量不超过10个,url必须以http或者https开头,域名部分最多不超过256个字符,path部分最多不超过1024个字符),并将文件上传权限设为公共读
BucketMirror bucketMirror = new BucketMirror(BucketMirror.Version.V3, false,
new BucketMirror.AsyncMirrorRule(Arrays.asList("http://www.example1.com", "https://www.example2.com"),
new BucketMirror.SavingSetting(CannedAccessControlList.PublicRead)),
null);
client.putBucketMirror("bucketName", bucketMirror);
/*5.16.4、新增或者更新同步回源规则,并设置回源文件的acl为PublicRead*/
List<BucketMirror.SyncMirrorRule> syncMirrorRules = new ArrayList<>();
//初始化同步回源规则对象
BucketMirror.SyncMirrorRule smr1 = new BucketMirror.SyncMirrorRule();
//设置源站url
smr1.setMirrorUrl("https://www.example1.com");
//设置文件上传权限设为公共读
smr1.setSavingSetting(new BucketMirror.SavingSetting(CannedAccessControlList.PublicRead));
syncMirrorRules.add(smr1);
BucketMirror bucketMirror = new BucketMirror(BucketMirror.Version.V3, false, null, syncMirrorRules);
client.putBucketMirror("bucketName", bucketMirror);
/*5.16.5、新增或者更新同步回源规则,并设置回源条件*/
List<BucketMirror.SyncMirrorRule> syncMirrorRules = new ArrayList<>();
//初始化同步回源规则对象
BucketMirror.SyncMirrorRule smr1 = new BucketMirror.SyncMirrorRule();
//设置源站url
smr1.setMirrorUrl("https://www.example1.com");
//设置回源触发条件,当http_codes为404,文件前缀为prefix_example时触发同步回源
smr1.setMatchCondition(new BucketMirror.MatchCondition(Arrays.asList("404"), Arrays.asList("prefix_example")));
syncMirrorRules.add(smr1);
BucketMirror bucketMirror = new BucketMirror(BucketMirror.Version.V3, false, null, syncMirrorRules);
client.putBucketMirror("bucketName", bucketMirror);
/*5.16.6、新增或者更新同步回源规则,并设置回源相关回源行为*/
List<BucketMirror.SyncMirrorRule> syncMirrorRules = new ArrayList<>();
BucketMirror.SyncMirrorRule smr1 = new BucketMirror.SyncMirrorRule();
smr1.setMirrorUrl("https://www.example1.com");
BucketMirror.MirrorRequestSetting setting = new BucketMirror.MirrorRequestSetting();
// 是否响应源站的3xx,如果为true则当源站返回3xx时,会向3xx location再次发送请求
// 如果为false,则当源站响应3xx时,ks3会向客户端响应424
setting.setFollow3xx(true);
// 是否将客户端传给ks3的query string透传给源站
setting.setPassQueryString(true);
// 设置回源时header的传送规则
BucketMirror.HeaderSetting hs = new BucketMirror.HeaderSetting();
// 将客户端请求ks3时所有的header都透传给源站
hs.setPassAll(true);
// 将一组指定了key的header透传给源站,目前暂不支持指定value
hs.setPassHeaders(Arrays.asList(new BucketMirror.Header("key1"), new BucketMirror.Header("key2")));
// 指定key的header不要透传给源站,目前暂不支持指定value
// 通常与 hs.setPassAll(true) 或者 hs.setPassHeaders()配合使用
hs.setRemoveHeaders(Arrays.asList(new BucketMirror.Header("key1"), new BucketMirror.Header("key2")));
// 将一组具有固定值的header透传给源站,即无论客户端请求ks3时header什么内容,此处设置的这些header都会传给源站
hs.setSetHeaders(
Arrays.asList(new BucketMirror.Header("key1", "value1"), new BucketMirror.Header("key2", "value2")));
setting.setHeaderSetting(hs);
smr1.setMirrorRequestSetting(setting);
syncMirrorRules.add(smr1);
BucketMirror bucketMirror = new BucketMirror(BucketMirror.Version.V3, false, null, syncMirrorRules);
client.putBucketMirror("bucketName", bucketMirror);
BucketMirror bucketMirror = client.getBucketMirror("bucketName");
client.deleteBucketMirror("bucketName");
/**
* 列出一个bucket下的object,返回的最大数为1000条
*/
public ObjectListing listObjectsSimple(){
ObjectListing list = client.listObjects("<您的bucket名称>");
return list;
}
/**
* 将列出bucket下满足object key前缀为指定字符串的object,返回的最大数为1000条
*/
public ObjectListing listObjectsWithPrefix(){
ObjectListing list = client.listObjects("<您的bucket名称>","<object key前缀>");
return list;
}
/**
* 自己调节列出object的参数,
*/
public ObjectListing listObjectsUseRequest(){
ObjectListing list = null;
//新建一个ListObjectsRequest
ListObjectsRequest request = new ListObjectsRequest("<您的bucket名称>");
//设置参数
request.setMaxKeys("<max keys>");//指定返回条数最大值
request.setPrefix("<object key前缀>");//返回以指定前缀开头的object
request.setDelimiter("<delimiter>");//设置文件分隔符,系统将根据该分隔符组织文件夹结构,默认是"/"
//执行操作
list = client.listObjects(request);
return list;
}
/**
* 使用循环列出所有object
*/
public void listAllObjects(){
ObjectListing list = null;
//初始化一个请求
ListObjectsRequest request = new ListObjectsRequest("<您的bucket名称>");
do{
//isTruncated为true时表示之后还有object,所以应该继续循环
if(list!=null&&list.isTruncated())
//在ObjectListing中将返回下次请求的marker
//如果请求的时候没有设置delimiter,则不会返回nextMarker,需要使用上一次list的最后一个key做为nextMarker
request.setMarker(list.getObjectSummaries().get(list.getObjectSummaries().size() - 1).getKey());
list = client.listObjects(request);
}while(list.isTruncated());
}
注意:
- 单次list,最多只可能返回1000个文件。如果需要返回更多,请通过marker参数设置游标。
- 具体输入输出参数说明详见API文档。
通过文件上传
/**
*将new File("<filePath>")这个文件上传至<bucket名称>这个存储空间下,并命名为<object key>
*/
public void putObjectSimple(){
PutObjectRequest request = new PutObjectRequest("<bucket名称>",
"<object key>", new File("<filePath>"));
//上传一个公开文件
//request.setCannedAcl(CannedAccessControlList.PublicRead);
client.putObject(request);
}
通过流上传
/**
*将一个输入流中的内容上传至<bucket名称>这个存储空间下,并命名为<object key>
*/
public void putObjectSimple(){
ObjectMetadata meta = new ObjectMetadata();
PutObjectRequest request = new PutObjectRequest("<bucket名称>",
"<object key>", new ByteArrayInputStream("1234".getBytes()),
meta);
// 可以指定内容的长度,否则程序会把整个输入流缓存起来,可能导致jvm内存溢出
meta.setContentLength(4);
client.putObject(request);
}
上传文件时设置存储类型
//设置为标准存储
request.setStorageClass(StorageClass.Standard);
//设置为低频存储
request.setStorageClass(StorageClass.StandardInfrequentAccess);
//设置为归档存储
request.setStorageClass(StorageClass.Archive);
上传文件时设置元数据
ObjectMetadata meta = new ObjectMetadata();
// 设置将要上传的object的用户元数据。当下载文件的时候,返回的header中将会带上设置的用户元数据。
//meta.setUserMeta("x-kss-meta-example", "example");
//设置将要上传的object的元数据。
//如果用户设置了Content-Type,那么在下载的时候,返回的header中将会带上Content-Type:{设置的值} 。建议根据文件实际类型做相应设置,方便浏览器识别。
//meta.setContentType("text/html");
request.setObjectMeta(meta);
设置回调
//如果用户设置了回调,那么当文件即将上传成功时,KS3将会使用POST的方式调用用户提供的回调地址,如果调用成功且用户处理成功(用户处理成功指返回{"result":true}给KS3,处理失败返回{"result":false}),那么文件才会真正的上传成功,如果调用失败或者用户处理失败,那么文件最终会上传失败。用户可以设置KS3回调时的body,最后KS3将会把用户提供的参数组织成json格式返回给用户。在下面示例中,KS3回调时的body为{"bucket":<实际存储的bucket>,"createTime":<文件创建时间>,"etag":<文件的etag,即文件的MD5经hex处理后的值>,"key":<文件实际保持的key>,"mimType":<文件的Content-Type>,"objectSize":<文件的字节数大小>,"time":"20150222"}
CallBackConfiguration config = new CallBackConfiguration();
config.setCallBackUrl("http://10.4.2.38:19090/");//KS3服务器回调的地址
//以下为KS3服务器访问http://10.4.2.38:19090/时body中的参数
Map<String,MagicVariables> magicVariables = new HashMap<String,MagicVariables>();
magicVariables.put("bucket", MagicVariables.bucket);
magicVariables.put("createTime", MagicVariables.createTime);
magicVariables.put("etag", MagicVariables.etag);
magicVariables.put("key", MagicVariables.key);
magicVariables.put("mimeType", MagicVariables.mimeType);
magicVariables.put("objectSize", MagicVariables.objectSize);
config.setBodyMagicVariables(magicVariables);
//用户可以自己定义返回的参数。
Map<String,String> kssVariables = new HashMap<String,String>();
kssVariables.put("time", "20150222");
request.setCallBackConfiguration(config);
设置服务端加密
ObjectMetadata meta = new ObjectMetadata();
meta.setSseAlgorithm("AES256");
request.setObjectMeta(meta);
设置用户提供key的服务端加密
//生成一个秘钥,这个秘钥需要自己保存好,加密解密都需要
KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
symKeyGenerator.init(256);
SecretKey symKey = symKeyGenerator.generateKey();
SSECustomerKey ssec = new SSECustomerKey(symKey);
request.setSseCustomerKey(ssec);
注意: - 如何下载文件?详见6.7和6.8。 - 异步数据处理命令。 - 如果用户使用了用户提供key的服务端加密,则下载文件的时候,必须提供加密使用的key,否则将无法下载。
了解KS3表单上传协议:post object。以及表单上传签名认证方式:Policy Signature构建方法。
在KS3控制台->空间设置->CORS配置里配置CORS跨域规则。https://ks3.console.ksyun.com/console.html#/。CORS为跨域资源共享,当使用js跨域时,需要配置该规则。W3C文档https://www.w3.org/TR/cors/
使用js sdk上传文件https://github.com/ks3sdk/ks3-js-sdk 。
js sdk 中的policy和signature可以通过Java SDK的该方法计算出来。
/**
如果用户对KS3协议不是特别清楚,建议使用该方法。每次上传的时候都去获取一次最新的签名信息
*/
public PostObjectFormFields postObjectSimple(){
/**
* 需要用户在postData和unknowValueField中提供所有的除KSSAccessKeyId, signature, file, policy外的所有表单项。否则用生成的签名上传会返回403</br>
* 对于用户可以确定表单值的放在 postData中,对于用户无法确定表单值的放在unknownValueField中(比如有的上传控件会添加一些表单项,但表单项的值可能是随机的)</br>
*
*/
Map<String,String> postData = new HashMap<String,String>();
//如果使用js sdk上传的时候设置了ACL,请提供以下一行,且值要与SDK中一致,否则删除下面一行代码
postData.put("acl","public-read");
//提供js sdk中的key值
postData.put("key","20150115/中文/${filename}");
List<String> unknowValueField = new ArrayList<String>();
//js sdk上传的时候会自动加上一个name的表单项,所以下面需要加上这样的代码。
unknowValueField.add("name");
//如果计算签名时提供的key里不包含${filename}占位符,可以把第二个参数传一个空字符串。因为计算policy需要的key是把${filename}进行替换后的key。
PostObjectFormFields fields = client.postObject("<您的bucket名称>", "<要上传的文件名称,不包含路径信息>", postData, unknowValueField);
fields.getKssAccessKeyId();
fields.getPolicy();
fields.getSignature();
return fields;
}
常见问题:
- 上传时,浏览器一共会发送两个请求,第一个为OPTIONS请求,第二个为POST请求。如果第一个请求返回403,请检测CORS配置是否正确。如果第二个请求返回403,请检查生成policy时,是否完全按照表单内容生成。或者把policy进行base64解码,对比表单内容是否正确。对比规则: Policy、Signature构建方法
public void copyObject(){
//将sourceBucket这个存储空间下的sourceKey这个object复制到destinationBucket这个存储空间下,并命名为destinationObject
CopyObjectRequest request = new CopyObjectRequest("destinationBucket","destinationObject","sourceBucket","sourceKey");
client.copyObject(request);
}
copy后的文件以服务端加密方式存储
ObjectMetadata meta = new ObjectMetadata();
meta.setSseAlgorithm("AES256");
request.setNewObjectMetadata(meta);
copy的文件以用户提供key的方式进行服务端加密,并设置新的文件的服务端加密
//生成一个密钥,这个密钥需要自己保存好,加密解密都需要
KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
symKeyGenerator.init(256);
SecretKey destKey= symKeyGenerator.generateKey();
SecretKey sourceKey= ??//当初加密时用的key
//指定被拷贝的数据是用sourceKey进行加密的,拷贝时将用该key先对数据解密
request.setSourceSSECustomerKey(new SSECustomerKey(sourceKey));
//指定拷贝生成的新数据的加密方式
request.setDestinationSSECustomerKey(new SSECustomerKey(destKey));
注意:
- 文件复制的时候,需要用户对源文件有读取权限,对目标bucket有写权限。
- 如果目标文件已经存在,复制操作将会抛出400,对应的ErrorCode是 invalidKey。
public HeadObjectResult headObject() {
HeadObjectRequest request = new HeadObjectRequest("<bucket名称>",
"<object名称>");
/**
* <p>
* 如果抛出{@link NotFoundException} 表示这个object不存在
* </p>
* <p>
* 如果抛出{@link AccessDinedException} 表示当前用户没有权限访问
* </p>
*/
HeadObjectResult result = client.headObject(request);
// head请求可以用于获取object的元数据
result.getObjectMetadata();
return result;
}
/**
* 判断一个object是否存在
*/
public boolean objectExists() {
try {
HeadObjectRequest request = new HeadObjectRequest("<bucket名称>",
"<object名称>");
client.headObject(request);
return true;
} catch (NotFoundException e) {
return false;
}
}
public GetObjectResult getObject(){
GetObjectRequest request = new GetObjectRequest("<bucket名称>","<object key>");
//重写返回的header
ResponseHeaderOverrides overrides = new ResponseHeaderOverrides();
overrides.setContentType("text/html");
//.......
request.setOverrides(overrides);
//只接受数据的0-10字节。通过控制该项可以实现分块下载
//request.setRange(0,10);
GetObjectResult result = client.getObject(request);
Ks3Object object = result.getObject();
//获取object的元数据
ObjectMetadata meta = object.getObjectMetadata();
//当分块下载时获取文件的实际大小,而非当前小块的大小
Long length = meta.getInstanceLength();
//获取object的输入流
object.getObjectContent();
return result;
}
如果文件是通过用户提供key的方式进行服务端加密的
SecretKey sourceKey= ??//当初加密时用的key
request.setSseCustomerKey(new SSECustomerKey(sourceKey));
通过:http://{bucket}.{endpoint}/{key}
的方式拼接一个URL即可。比如:http://test-bucket.ks3-cn-beijing.ksyuncs.com/2015/10/19/image.jpg
,该URL中的{bucket}是test-bucket,{endpoint}是kssws.ks-cdn.com,{key}是2015/10/19/image.jpg。
通过以下代码可以生成一个访问地址
//生成一个在1000秒后过期的外链
client.generatePresignedUrl(<bucket>,<key>,1000);
//生成一个1000秒后过期并重写返回的heade的外链
ResponseHeaderOverrides overrides = new ResponseHeaderOverrides();
//overrides.setContentType("text/html");
//.......
client.generatePresignedUrl(<bucket>,<key>,1000,overrides);
常见问题: - 如果文件不存在,会返回NoSuchKey错误 - 如果以公开的方式访问私有文件,会返回AccessDined错误 - 如果私有文件访问地址过期,会返回URLExpired错误 - 1000秒后过期是参照客户端本地时间的。
object的访问权限说明可以参考: ACL(访问控制列表) 。
/**
*通过预设的ACL设置object的访问权限,预设的ACL包括:private:私有。public-read:为所有用户授予read权限。
*/
public void putObjectAclWithCannedAcl(){
PutObjectACLRequest request = new PutObjectACLRequest("<bucket名称>","<object名称>");
//设为私有
request.setCannedAcl(CannedAccessControlList.Private);
//设为公开读
//request.setCannedAcl(CannedAccessControlList.PublicRead);
client.putObjectACL(request);
}
/**
*自定义文件访问权限
*/
public void putObjectAclWithAcl(){
PutObjectACLRequest request = new PutObjectACLRequest("<bucket名称>","<objectkey>");
AccessControlList acl = new AccessControlList();
//设置用户id为12345678的用户对object的读权限
Grant grant1 = new Grant();
grant1.setGrantee(new GranteeId("12345678"));
grant1.setPermission(Permission.Read);
acl.addGrant(grant1);
//设置用户id为123456789的用户对object完全控制
Grant grant2 = new Grant();
grant2.setGrantee(new GranteeId("123456789"));
grant2.setPermission(Permission.FullControl);
acl.addGrant(grant2);
//设置用户id为12345678910的用户对object的写权限
Grant grant3 = new Grant();
grant3.setGrantee(new GranteeId("12345678910"));
grant3.setPermission(Permission.Write);
acl.addGrant(grant3);
request.setAccessControlList(acl);
client.putObjectACL(request);
}
public AccessControlPolicy getObjectAcl(){
/**
* 获取 <bucket名称>这个bucket下<object key>的权限控制信息
*/
AccessControlPolicy policy = client.getObjectACL("<bucket名称>","<object key>");
return policy;
}
说明:
- AccessControlPolicy中有public CannedAccessControlList getCannedAccessControlList()方法可以从ACL中提取出预设ACL来。
/**
* 将<bucket名称>这个存储空间下的<object key>删除
*/
public void deleteObject(){
client.deleteObject("<bucket名称>","<object key>");
}
/**
* 解冻<bucket名称>这个存储空间下的<object key>文件
*/
public RestoreObjectResult restoreObject(){
RestoreObjectResult result = client.restoreObject("<bucket名称>","<object key>");
//获取<object key>文件的存储类型,类型如下
//标准(StorageClass.Standard)、低频(StorageClass.StandardInfrequentAccess)、归档(StorageClass.Archive)
result.getCls();
//获取<object key>文件的解冻状态,状态如下
//解冻成功(RestoreCycle.RESTORE)、正在解冻中(RestoreCycle.RESTORING)、已解冻(RestoreCycle.RESTORED)
result.getType();
return result;
}
//修改文件元数据信息
public void modifyObjMetadata(){
/**修改yourbucket这个存储空间下的yourkey这个object的元数据信息*/
CopyObjectRequest request = new CopyObjectRequest("yourbucket", "yourkey", "yourbucket", "yourkey");
ObjectMetadata meta = new ObjectMetadata();
meta.setHeader("x-kss-metadata-directive", "REPLACE");
meta.setContentType("application/json");
meta.setUserMeta("x-kss-meta-zlymeta123", "mymeta123");
request.setNewObjectMetadata(meta);
CopyResult result = client.copyObject(request);
}
//修改文件存储类型
public void modifyObjStorageClass(){
/**修改yourbucket这个存储空间下的yourkey这个object的存储类型为低频存储*/
// 创建CopyObjectRequest对象,填入bucket和object的名称
CopyObjectRequest request = new CopyObjectRequest("yourbucket", "yourkey", "yourbucket", "yourkey");
// 设置文件存储类型为低频
request.setStorageClass(StorageClass.StandardInfrequentAccess);
// 更改文件存储类型
CopyResult result = client.copyObject(request);
}
public Ks3Result putObjectFetch(){
//设置需要fetch object请求的,bucket、文件名以及源站地址
PutObjectFetchRequest request = new PutObjectFetchRequest(bucketName,key,sourceUrl);
//将文件设置为公开读
request.setCannedAcl(CannedAccessControlList.PublicRead);
//base64加密MD5信息,128位,用于对象完整性校验
request.setMd5Base64(md5);
ObjectMetadata metadata = new ObjectMetadata();
//设置文件的元数据
request.setObjectMeta(metadata);
//设置回调,fetchObject是异步操作,不会马上感知是否成功,需要指定上传成功或失败时的回调URL
request.setCallbackUrl(callbackurl);
Ks3Result result = client.putObjectFetch(request);
return result;
}
public void putObjectTagging() {
ObjectTag tag = new ObjectTag("aaa", "111");
ObjectTag tag1 = new ObjectTag("as_=/+-:23", null);
ObjectTag tag2 = new ObjectTag("tt", "aa");
ObjectTag tag3 = new ObjectTag("asdfasdf", "aa");
List<ObjectTag> list = new ArrayList<>();
list.add(tag);
//list.add(tag1);
//list.add(tag2);
//list.add(tag3);
ObjectTagging tagging = new ObjectTagging(list);
PutObjectTaggingRequest request = new PutObjectTaggingRequest(bucket, "txt0", tagging);
client.putObjectTagging(request);
}
public void putObjectWithTagging() {
PutObjectRequest request = new PutObjectRequest(bucket, "tag", new File("D:\\upload-test\\1.txt"));
ObjectMetadata metadata = new ObjectMetadata();
metadata.setHeader("x-kss-tagging", "aaa=111");
request.setObjectMeta(metadata);
PutObjectResult result = client.putObject(request);
System.out.println(result.getOriginResponse());
}
public void getObjectTagging() {
GetObjectTaggingRequest request = new GetObjectTaggingRequest(bucket, "txt0");
ObjectTagging tagging = client.getObjectTagging(request);
System.out.println(JSON.toJSONString(tagging));
}
public void deleteObjectTagging() {
DeleteObjectTaggingRequest request = new DeleteObjectTaggingRequest(bucket, "objecttagnew");
client.deleteObjectTagging(request);
}
分块上传流程:
初始化分块上传:7.2
多次调用上传块:7.3
完成分块上传:7.5(如果中途失败,则取消分块上传:7.6)
public ListMultipartUploadsResult listMultipartUploads() {
ListMultipartUploadsRequest request = new ListMultipartUploadsRequest("test.bucket");
/**
* keyMarker为空,uploadIdMarker不为空
* 无意义
*
* keyMarker不为空,uploadIdMarker不为空
* 列出分块上传object key为keyMarker,且upload id 字典排序大于uploadIdMarker的结果
*
* keyMarker不为空,uploadIdMarker为空
* 列出分块上传object key字典排序大于keyMarker的结果
*/
request.setKeyMarker("keyMarker");
request.setUploadIdMarker("uploadIdMarker");
/**
* prefix和delimiter详解
*
* commonPrefix由prefix和delimiter确定,以prefix开头的object
* key,在prefix之后第一次出现delimiter的位置之前(包含delimiter)的子字符串将存在于commonPrefixes中
* 比如有以下几个分块上传
*
* aaaa/bbb/ddd.txt
* aaaa/ccc/eee.txt
* ssss/eee/fff.txt
*
* prefix为空 delimiter为/
* 则commonPrefix 为 aaaa/和ssss/ 返回的uploads为空
*
* prefix为aaaa/ delimiter为/
* 则commonPrefix 为 aaaa/bbb/和aaaa/ccc/ 返回的uploads为空
*
* prefix为ssss/ delimiter为/
* 则commonPrefix 为 aaaa/eee/ 返回的uploads为空
*
* prefix为空 delimiter为空
* 则commonPrefix 为空 返回的uploads为aaaa/bbb/ddd.txt、aaaa/ccc/eee.txt、ssss/eee/fff.txt
*
* prefix为aaaa/ delimiter为空
* 则commonPrefix 为空 返回的uploads为aaaa/bbb/ddd.txt、aaaa/ccc/eee.txt
*
* prefix为ssss/ delimiter为空
* 则commonPrefix 为空 返回的uploads为ssss/eee/fff.txt
* </p>
*/
request.setDelimiter("/");
request.setPrefix("prefix");
request.setMaxUploads(100);// 最多返回100条记录,默认(最大)是1000
ListMultipartUploadsResult result = client
.listMultipartUploads(request);
return result;
}
说明:
- 最多只能返回1000条记录。
- keyMarker、uploadIdMarker、delimiter、prefix、maxUploads都不是必要参数。用户可以根据自己的实际情况调节。
- 当返回的result.isTruncated()为true时,需要用返回的result.getNextKeyMarker()和result.getNextUploadIdMarker参数作为keyMarker和uploadIdMarker继续罗列之后的分块上传。
public InitiateMultipartUploadResult initMultipartUpload(){
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(
bucket, key);
//设置为公开读
request.setCannedAcl(CannedAccessControlList.PublicRead);
InitiateMultipartUploadResult result = client
.initiateMultipartUpload(request);
result.getUploadId();//获取初始化的uploadId,之后的操作中将会用到。
return result;
}
初始化分块上传时设置元数据
ObjectMetadata meta = new ObjectMetadata();
// 设置将要上传的object的用户元数据。当下载文件的时候,返回的header中将会带上设置的用户元数据。
//meta.setUserMeta("x-kss-meta-example", "example");
//设置将要上传的object的元数据。
//如果用户设置了Content-Type,那么在下载的时候,返回的header中将会带上Content-Type:{设置的值} 。建议根据文件实际类型做相应设置,方便浏览器识别。
//meta.setContentType("text/html");
request.setObjectMeta(meta);
设置服务端加密
ObjectMetadata meta = new ObjectMetadata();
meta.setSseAlgorithm("AES256");
request.setObjectMeta(meta);
设置用户提供key的服务端加密
//生成一个密钥,这个密钥需要自己保存好,加密解密都需要
KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
symKeyGenerator.init(256);
SecretKey symKey = symKeyGenerator.generateKey();
SSECustomerKey ssec = new SSECustomerKey(symKey);
request.setSseCustomerKey(ssec);
说明:
- 通过result.getUploadId()可以获取到本次上传的uploadId,之后的操作中将到会用到该uploadId。
- 如何下载文件?详见6.7和6.8。
- 如果用户使用了用户提供key的服务端加密,则下载文件的时候,必须提供加密使用的key,否则将无法下载。
提供文件及partSize和offset参数的方式上传。
public PartETag uploadPart(){
UploadPartRequest request = new UploadPartRequest(
{bucket}, {key} ,{uploadId},
{partNumber},{要上传的文件}, {partSize},{offset});
//可以指定内容的MD5值,否则程序只会在客户端进行MD5校验。如果指定的话会在服务端进行MD5校验
//request.setContentMd5("yE0ZQBpRgPrLSDEe6fGAvg==");
PartETag result = client.uploadPart(request);
return result;
}
提供输入流和partSize的方式上传。
public PartETag uploadPart(){
InputStream content = new ByteArrayInputStream("content".getBytes());
UploadPartRequest request = new UploadPartRequest(
{bucket}, {key} ,{uploadId},
{partNumber},content,{partSize});
//可以指定内容的MD5值,否则程序只会在客户端进行MD5校验。如果指定的话会在服务端进行MD5校验
//request.setContentMD5("yE0ZQBpRgPrLSDEe6fGAvg==");
PartETag result = client.uploadPart(request);
return result;
}
参数说明:
- bucket:初始化分块上传时的bucket。
- key:初始化分块上传时的key。
- uploadId:初始化分块上传获取到的uploadId。
- partNumber:当前上传的块的序号,需要为连续的整数。
- partSize:当前上传的块的字节数。
设置用户提供key的服务端加密
//当init multipart upload时指定了用户提供key的服务端加密,在upload part时也需要指定相同的加密信息
SecretKey symKey = ??//init时的key
SSECustomerKey ssec = new SSECustomerKey(symKey);
request.setSseCustomerKey(ssec);
说明:
- partNumber需要是连续的整数。
- 用户需要在自己程序中保存每次分块上传返回的partNumber和ETag,在之后的完成分块上传时需要用到。
7.4 罗列已经上传的块
罗列一个uploadId已经上传的块。
public ListPartsResult listParts(){
ListPartsRequest request = new ListPartsRequest({bucket}, {key} ,{uploadId});
ListPartsResult tags = client.listParts(request);
return tags;
}
参数说明:
- bucket:初始化分块上传时的bucket。
- key:初始化分块上传时的key。
- uploadId:初始化分块上传获取到的uploadId。
说明:
- 该接口最多只会返回1000条记录,如果总数大于1000,请使用request.setPartNumberMarker()方法设置partNumber的起始位置。
该接口将完成分块上传,把所有的块合并成一个文件。
public void completaMultipartUpload(){
List<PartETag> parts = new ArrayList<PartETag>();
//把上传块时返回的所有要合并的块依次添加进来
//parts.add(...)
CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest({bucket},{key},{uploadId}, parts);
client.completeMultipartUpload(request);
}
设置回调
//如果用户设置了回调,那么当文件即将上传成功时,KS3将会使用POST的方式调用用户提供的回调地址,如果调用成功且用户处理成功(用户处理成功指返回{"result":true}给KS3,处理失败返回{"result":false}),那么文件才会真正的上传成功,如果调用失败或者用户处理失败,那么文件最终会上传失败。用户可以设置KS3回调时的body,最后KS3将会把用户提供的参数组织成json格式返回给用户。在下面示例中,KS3回调时的body为{"bucket":<实际存储的bucket>,"createTime":<文件创建时间>,"etag":<文件的etag,即文件的MD5经hex处理后的值>,"key":<文件实际保持的key>,"mimType":<文件的Content-Type>,"objectSize":<文件的字节数大小>,"time":"20150222"}
CallBackConfiguration config = new CallBackConfiguration();
config.setCallBackUrl("http://10.4.2.38:19090/");//KS3服务器回调的地址
//以下为KS3服务器访问http://10.4.2.38:19090/时body中的参数
Map<String,MagicVariables> magicVariables = new HashMap<String,MagicVariables>();
magicVariables.put("bucket", MagicVariables.bucket);
magicVariables.put("createTime", MagicVariables.createTime);
magicVariables.put("etag", MagicVariables.etag);
magicVariables.put("key", MagicVariables.key);
magicVariables.put("mimeType", MagicVariables.mimeType);
magicVariables.put("objectSize", MagicVariables.objectSize);
config.setBodyMagicVariables(magicVariables);
//用户可以自己定义返回的参数。
Map<String,String> kssVariables = new HashMap<String,String>();
kssVariables.put("time", "20150222");
request.setCallBackConfiguration(config);
对于不再使用的分块上传,建议调用该接口取消。
public void abortMultipartUpload(){
client.abortMultipartUpload(bucketname, objectkey, uploadId);
}
根据图片处理API和图片样式管理在原有key的基础上拼接图片处理参数。比如:{原key}@base@tag=imageInfo 或者 {原key}@style@{样式名称}。
使用拼接好的key,根据6.8获取文件访问地址即可。
GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest();
req.setMethod(HttpMethod.GET);
req.setBucket("<bucket名称>");
req.setKey("key");
req.setExpiration(<生成的外链的过期时间>);//unix时间戳,不指定的话则默认为15分钟后过期
ResponseHeaderOverrides overrides = new ResponseHeaderOverrides();
//overrides.setContentType("application/xml");//设置返回的Content-Type
req.setResponseHeaders(overrides);
String url = client.generatePresignedUrl(req);
KS3上传协议详见:put object
GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest();
req.setMethod(HttpMethod.PUT);
req.setBucket(bucket);//文件上传的bucket
req.setKey(key);//文件名
req.setExpiration(<生成的外链的过期时间>);//不指定的话则默认为15分钟后过期
req.getRequestConfig().getExtendHeaders().put("x-kss-acl", "public-read");//设置acl为公开读,不加该header则默认为私有,生成外链时设置了header,则在使用外链的时候也需要添加相应的header
req.setContentType("application/ocet-stream");//设置文件的Content-Type,具体值请根据时间情况设定。在使用外链的时候需要把Content-Type设置成指定的值
//req.setSseAlgorithm("AES256");//设置服务端加密
String url = client.generatePresignedUrl(req);
当用户拿到该URL之后便可以通过以下请求上传文件(请自行拆分uri和host)
PUT /{uri} HTTP/1.1
Host: {host}
Date: {当前时间}
x-kss-acl:public-read //因为在生成外链的时候设置了该header,所以在发送的时候也需要带上
Content-Type:application/ocet-stream //同上
Content-Length:100
[100 bytes of data]
用户可以使用sdk将数据加密后再上传到ks3
下载UnlimitedJCEPolicyJDK7,将local_policy.jar和US_export_policy.jar放在{JAVA_HOME}\jre\lib\security目录下,覆盖原有的。
下载bcprov-jdk15on-152.jar,放在{JAVA_HOME}\jre\lib\ext目录下。
使用对称主密钥
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.junit.Assert;
public class GenerateSymmetricMasterKey {
private static final String keyDir = System.getProperty("java.io.tmpdir");
private static final String keyName = "secret.key";
public static void main(String[] args) throws Exception {
//Generate symmetric 256 bit AES key.
KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
symKeyGenerator.init(256);
SecretKey symKey = symKeyGenerator.generateKey();
//Save key.
saveSymmetricKey(keyDir, symKey);
//Load key.
SecretKey symKeyLoaded = loadSymmetricAESKey(keyDir, "AES");
Assert.assertTrue(Arrays.equals(symKey.getEncoded(), symKeyLoaded.getEncoded()));
}
public static void saveSymmetricKey(String path, SecretKey secretKey)
throws IOException {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
secretKey.getEncoded());
FileOutputStream keyfos = new FileOutputStream(path + "/" + keyName);
keyfos.write(x509EncodedKeySpec.getEncoded());
keyfos.close();
}
public static SecretKey loadSymmetricAESKey(String path, String algorithm)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException{
//Read private key from file.
File keyFile = new File(path + "/" + keyName);
FileInputStream keyfis = new FileInputStream(keyFile);
byte[] encodedPrivateKey = new byte[(int)keyFile.length()];
keyfis.read(encodedPrivateKey);
keyfis.close();
//Generate secret key.
return new SecretKeySpec(encodedPrivateKey, "AES");
}
}
使用非对称主密钥
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
public class GenerateAsymmetricMasterKey {
private static final String keyDir = System.getProperty("java.io.tmpdir");
private static final SecureRandom srand = new SecureRandom();
public static void main(String[] args) throws Exception {
// Generate RSA key pair of 1024 bits
KeyPair keypair = genKeyPair("RSA", 1024);
// Save to file system
saveKeyPair(keyDir, keypair);
// Loads from file system
KeyPair loaded = loadKeyPair(keyDir, "RSA");
// Sanity check
assertTrue(Arrays.equals(keypair.getPublic().getEncoded(), loaded
.getPublic().getEncoded()));
assertTrue(Arrays.equals(keypair.getPrivate().getEncoded(), loaded
.getPrivate().getEncoded()));
}
public static KeyPair genKeyPair(String algorithm, int bitLength)
throws NoSuchAlgorithmException {
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(algorithm);
keyGenerator.initialize(1024, srand);
return keyGenerator.generateKeyPair();
}
public static void saveKeyPair(String dir, KeyPair keyPair)
throws IOException {
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
publicKey.getEncoded());
FileOutputStream fos = new FileOutputStream(dir + "/public.key");
fos.write(x509EncodedKeySpec.getEncoded());
fos.close();
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
privateKey.getEncoded());
fos = new FileOutputStream(dir + "/private.key");
fos.write(pkcs8EncodedKeySpec.getEncoded());
fos.close();
}
public static KeyPair loadKeyPair(String path, String algorithm)
throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException {
// read public key from file
File filePublicKey = new File(path + "/public.key");
FileInputStream fis = new FileInputStream(filePublicKey);
byte[] encodedPublicKey = new byte[(int) filePublicKey.length()];
fis.read(encodedPublicKey);
fis.close();
// read private key from file
File filePrivateKey = new File(path + "/private.key");
fis = new FileInputStream(filePrivateKey);
byte[] encodedPrivateKey = new byte[(int) filePrivateKey.length()];
fis.read(encodedPrivateKey);
fis.close();
// Convert them into KeyPair
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
encodedPublicKey);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
encodedPrivateKey);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
return new KeyPair(publicKey, privateKey);
}
}
SecretKey symKey = ??//主密钥
EncryptionMaterials keyMaterials = new EncryptionMaterials(symKey);
Ks3 client = new Ks3EncryptionClient("<accesskeyid>","<accesskeysecret>",keyMaterials);
上传上去的文件是经过加密的。
下载文件只能通过该客户端getObject方法下载,用其他方法下载下来的文件是经过加密的。
分块上传时必须依次上传每一块。当上传最后一块时必须通过request.setLastPart指定最后一块。上传顺序不能错乱,不能使用多线程分块上传。
请妥善保管自己的主密钥,如果主密钥丢失,将无法解密数据。
纯净模式