11package com .qiniu .storage ;
22
3+ import com .google .gson .JsonNull ;
4+ import com .google .gson .JsonObject ;
35import com .qiniu .common .Constants ;
46import com .qiniu .common .QiniuException ;
57import com .qiniu .http .Client ;
68import com .qiniu .http .Response ;
79import com .qiniu .storage .model .*;
810import com .qiniu .util .*;
911
10- import java .util .ArrayList ;
11- import java .util .Iterator ;
12- import java .util .Map ;
13-
12+ import java .util .*;
1413
1514/**
1615 * 主要涉及了空间资源管理及批量操作接口的实现,具体的接口规格可以参考
1716 * 参考文档:<a href="http://developer.qiniu.com/kodo/api/rs">资源管理</a>
1817 */
1918public final class BucketManager {
19+
2020 /**
2121 * Auth 对象
2222 * 该类需要使用QBox鉴权,所以需要指定Auth对象
@@ -27,7 +27,6 @@ public final class BucketManager {
2727 * Configuration 对象
2828 * 该类相关的域名配置,解析配置,HTTP请求超时时间设置等
2929 */
30-
3130 private Configuration configuration ;
3231
3332 /**
@@ -53,7 +52,6 @@ public BucketManager(Auth auth, Client client) {
5352 this .client = client ;
5453 }
5554
56-
5755 /**
5856 * EncodedEntryURI格式,其中 bucket+":"+key 称之为 entry
5957 *
@@ -83,7 +81,6 @@ public static String encodedEntry(String bucket) {
8381 return encodedEntry (bucket , null );
8482 }
8583
86-
8784 /**
8885 * 获取账号下所有空间名称列表
8986 *
@@ -114,7 +111,6 @@ public void deleteBucket(String bucketname) throws QiniuException {
114111 * @return 该空间名下的domain
115112 * @throws QiniuException
116113 */
117-
118114 public String [] domainList (String bucket ) throws QiniuException {
119115 String url = String .format ("%s/v6/domain/list?tbl=%s" , configuration .apiHost (), bucket );
120116 Response r = get (url );
@@ -145,8 +141,14 @@ public FileListIterator createFileListIterator(String bucket, String prefix, int
145141 return new FileListIterator (bucket , prefix , limit , delimiter );
146142 }
147143
144+ private String listQuery (String bucket , String prefix , String marker , int limit , String delimiter ) {
145+ StringMap map = new StringMap ().put ("bucket" , bucket ).putNotEmpty ("marker" , marker )
146+ .putNotEmpty ("prefix" , prefix ).putNotEmpty ("delimiter" , delimiter ).putWhen ("limit" , limit , limit > 0 );
147+ return map .formString ();
148+ }
149+
148150 /**
149- * 根据前缀获取文件列表
151+ * 列举空间文件 v1 接口,返回一个 response 对象。
150152 *
151153 * @param bucket 空间名
152154 * @param prefix 文件名前缀
@@ -156,16 +158,69 @@ public FileListIterator createFileListIterator(String bucket, String prefix, int
156158 * @return
157159 * @throws QiniuException
158160 */
161+ public Response listV1 (String bucket , String prefix , String marker , int limit , String delimiter )
162+ throws QiniuException {
163+ String url = String .format ("%s/list?%s" , configuration .rsfHost (auth .accessKey , bucket ),
164+ listQuery (bucket , prefix , marker , limit , delimiter ));
165+ return get (url );
166+ }
167+
159168 public FileListing listFiles (String bucket , String prefix , String marker , int limit , String delimiter )
160169 throws QiniuException {
161- StringMap map = new StringMap ().put ("bucket" , bucket ).putNotEmpty ("marker" , marker )
162- .putNotEmpty ("prefix" , prefix ).putNotEmpty ("delimiter" , delimiter ).putWhen ("limit" , limit , limit > 0 );
170+ Response response = listV1 (bucket , prefix , marker , limit , delimiter );
171+ if (!response .isOK ()) {
172+ throw new QiniuException (response );
173+ }
174+ FileListing fileListing = response .jsonToObject (FileListing .class );
175+ response .close ();
176+ return fileListing ;
177+ }
163178
164- String url = String .format ("%s/list?%s" , configuration .rsfHost (auth .accessKey , bucket ), map .formString ());
165- Response r = get (url );
166- return r .jsonToObject (FileListing .class );
179+ /**
180+ * 列举空间文件 v2 接口,返回一个 response 对象。v2 接口可以避免由于大量删除导致的列举超时问题,返回的 response 对象中的 body 可以转换为
181+ * string stream 来处理。
182+ *
183+ * @param bucket 空间名
184+ * @param prefix 文件名前缀
185+ * @param marker 上一次获取文件列表时返回的 marker
186+ * @param limit 每次迭代的长度限制,推荐值 10000
187+ * @param delimiter 指定目录分隔符,列出所有公共前缀(模拟列出目录效果)。缺省值为空字符串
188+ * @return Response 返回一个 okhttp response 对象
189+ * @throws QiniuException
190+ */
191+ public Response listV2 (String bucket , String prefix , String marker , int limit , String delimiter )
192+ throws QiniuException {
193+ String url = String .format ("%s/v2/list?%s" , configuration .rsfHost (auth .accessKey , bucket ),
194+ listQuery (bucket , prefix , marker , limit , delimiter ));
195+ return get (url );
196+ }
197+
198+ public FileListing listFilesV2 (String bucket , String prefix , String marker , int limit , String delimiter )
199+ throws QiniuException {
200+ Response response = listV2 (bucket , prefix , marker , limit , delimiter );
201+ final String result = response .bodyString ();
202+ response .close ();
203+ List <String > lineList = Arrays .asList (result .split ("\n " ));
204+ FileListing fileListing = new FileListing ();
205+ List <FileInfo > fileInfoList = new ArrayList <>();
206+ Set <String > commonPrefixSet = new HashSet <>();
207+ for (int i = 0 ; i < lineList .size (); i ++) {
208+ String line = lineList .get (i );
209+ JsonObject jsonObject = Json .decode (line , JsonObject .class );
210+ if (!(jsonObject .get ("item" ) instanceof JsonNull ))
211+ fileInfoList .add (Json .decode (jsonObject .get ("item" ), FileInfo .class ));
212+ String dir = jsonObject .get ("dir" ).getAsString ();
213+ if (!"" .equals (dir )) commonPrefixSet .add (dir );
214+ if (i == lineList .size () - 1 )
215+ fileListing .marker = jsonObject .get ("marker" ).getAsString ();
216+ }
217+ fileListing .items = fileInfoList .toArray (new FileInfo []{});
218+ fileListing .commonPrefixes = commonPrefixSet .toArray (new String []{});
219+
220+ return fileListing ;
167221 }
168222
223+
169224 /**
170225 * 获取空间中文件的属性
171226 *
@@ -180,7 +235,6 @@ public FileInfo stat(String bucket, String fileKey) throws QiniuException {
180235 return r .jsonToObject (FileInfo .class );
181236 }
182237
183-
184238 /**
185239 * 删除指定空间、文件名的文件
186240 *
@@ -230,7 +284,6 @@ public Response changeHeaders(String bucket, String key, Map<String, String> hea
230284 return rsPost (bucket , path , null );
231285 }
232286
233-
234287 /**
235288 * 修改文件的类型(普通存储或低频存储)
236289 *
@@ -306,7 +359,6 @@ public void copy(String fromBucket, String fromFileKey, String toBucket, String
306359 copy (fromBucket , fromFileKey , toBucket , toFileKey , false );
307360 }
308361
309-
310362 /**
311363 * 移动文件,要求空间在同一账号下
312364 *
@@ -339,7 +391,6 @@ public Response move(String fromBucket, String fromFileKey, String toBucket, Str
339391 return move (fromBucket , fromFileKey , toBucket , toFileKey , false );
340392 }
341393
342-
343394 /**
344395 * 抓取指定地址的文件,以指定名称保存在指定空间
345396 * 要求指定url可访问,大文件不建议使用此接口抓取。可先下载再上传
@@ -381,7 +432,6 @@ public FetchRet fetch(String url, String bucket, String key) throws QiniuExcepti
381432 * @return Response
382433 * @throws QiniuException
383434 */
384-
385435 public Response asynFetch (String url , String bucket , String key ) throws QiniuException {
386436 String requesturl = configuration .apiHost (auth .accessKey , bucket ) + "/sisyphus/fetch" ;
387437 StringMap stringMap = new StringMap ().put ("url" , url ).put ("bucket" , bucket ).putNotNull ("key" , key );
@@ -520,18 +570,15 @@ public BucketInfo getBucketInfo(String bucket) throws QiniuException {
520570 return info ;
521571 }
522572
523-
524573 public void setIndexPage (String bucket , IndexPageType type ) throws QiniuException {
525574 String url = String .format ("%s/noIndexPage?bucket=%s&noIndexPage=%s" ,
526575 configuration .ucHost (), bucket , type .getType ());
527576 Response res = post (url , null );
528577 }
529578
530-
531579 /*
532580 * 相关请求的方法列表
533581 * */
534-
535582 private Response rsPost (String bucket , String path , byte [] body ) throws QiniuException {
536583 check (bucket );
537584 String url = configuration .rsHost (auth .accessKey , bucket ) + path ;
@@ -592,7 +639,6 @@ public BatchOperations() {
592639 /**
593640 * 添加chgm指令
594641 */
595-
596642 public BatchOperations addChgmOp (String bucket , String key , String newMimeType ) {
597643 String resource = encodedEntry (bucket , key );
598644 String encodedMime = UrlSafeBase64 .encodeToString (newMimeType );
0 commit comments