我正在使用Google Cloud Java API从Google Cloud Storage(GCS)中获取对象。此代码读取如下内容:

Storage storage = ...
List<StorageObject> storageObjects = storage.objects().list(bucket).execute().getItems();


但这不会返回GCS存储桶中的所有项目(存储对象),只会返回第一个“页面”中的前1000个项目。因此,为了获得下一个1000项,应该执行以下操作:

Storage.Objects.List list = storage.objects().list(bucket).execute();
String nextPageToken = objects.getNextPageToken();
List<StorageObject> itemsInFirstPage = objects.getItems();

if (nextPageToken != null) {
    // recurse
}


我想做的是在遍历GCS存储桶中的所有项之前找到与Predicate匹配的项,直到谓词匹配。为了提高效率,我只想在当前页面中找不到项目时才在下一页中加载项目。对于单个页面,此方法有效:

Predicate<StorageObject> matchesItem = ...
takeWhile(storage.objects().list(bucket).execute().getItems().stream(), not(matchesItem));


here复制takeWhile的位置。

这将递归地从所有页面加载存储对象:

private Stream<StorageObject> listGcsPageItems(String bucket, String pageToken) {
    if (pageToken == null) {
        return Stream.empty();
    }


    Storage.Objects.List list = storage.objects().list(bucket);
    if (!pageToken.equals(FIRST_PAGE)) {
        list.setPageToken(pageToken);
    }
    Objects objects = list.execute();
    String nextPageToken = objects.getNextPageToken();
    List<StorageObject> items = objects.getItems();
    return Stream.concat(items.stream(), listGcsPageItems(bucket, nextPageToken));
}


其中FIRST_PAGE只是一个“魔术” String,指示该方法不要设置特定页面(这将导致出现第一页项目)。

这种方法的问题在于它急切,即,在应用“匹配谓词”之前,要加载所有页面的所有项目。我希望这很懒(一次一页)。我该如何实现?

最佳答案

我将实现自定义Iterator<StorageObject>Supplier<StorageObject>,这将使当前页面列表和下一页标记保持在其内部状态,从而一个一个地生成StorageObject

然后,我将使用以下代码查找第一个匹配项:

Optional<StorageObject> result =
    Stream.generate(new StorageObjectSupplier(...))
        .filter(predicate)
        .findFirst();


仅在找到匹配项之前(即懒惰地),才调用供应商。

另一种方法是按页面实施供应商,即class StorageObjectPageSupplier implements Supplier<List<StorageObject>>并使用流API对其进行展平:

Optional<StorageObject> result =
    Stream.generate(new StorageObjectPageSupplier(...))
        .flatMap(List::stream)
        .filter(predicate)
        .findFirst();

09-28 05:03