22

我正在使用下面提到的代码从 s3 存储桶中获取所有文件名的列表。我在 s3 中有两个存储桶。对于下面的存储桶之一,代码返回所有文件名(超过 1000 个),但相同的代码仅返回另一个存储桶的 1000 个文件名。我只是不明白发生了什么。为什么为一个存储桶运行相同的代码而不为其他存储桶运行相同的代码?

我的桶也有层次结构文件夹/文件名.jpg。

ObjectListing objects = s3.listObjects("bucket.new.test");
do {
    for (S3ObjectSummary objectSummary : objects.getObjectSummaries()) {
        String key = objectSummary.getKey();
        System.out.println(key);
    }
    objects = s3.listNextBatchOfObjects(objects);
} while (objects.isTruncated());
4

8 回答 8

19

改进@Abhishek 的答案。这段代码稍微短一些,变量名是固定的。

您必须获取对象列表,将其内容添加到集合中,然后从列表中获取下一批对象。重复该操作,直到列表不会被截断。

List<S3ObjectSummary> keyList = new ArrayList<S3ObjectSummary>();
ObjectListing objects = s3.listObjects("bucket.new.test");
keyList.addAll(objects.getObjectSummaries());

while (objects.isTruncated()) {
    objects = s3.listNextBatchOfObjects(objects);
    keyList.addAll(objects.getObjectSummaries());
}
于 2015-01-13T21:28:32.770 回答
8

对于 Scala 开发人员,这里是使用官方AWS SDK for Java执行 AmazonS3 存储桶内容的完整扫描和映射的递归函数

import com.amazonaws.services.s3.AmazonS3Client
import com.amazonaws.services.s3.model.{S3ObjectSummary, ObjectListing, GetObjectRequest}
import scala.collection.JavaConversions.{collectionAsScalaIterable => asScala}

def map[T](s3: AmazonS3Client, bucket: String, prefix: String)(f: (S3ObjectSummary) => T) = {

  def scan(acc:List[T], listing:ObjectListing): List[T] = {
    val summaries = asScala[S3ObjectSummary](listing.getObjectSummaries())
    val mapped = (for (summary <- summaries) yield f(summary)).toList

    if (!listing.isTruncated) mapped.toList
    else scan(acc ::: mapped, s3.listNextBatchOfObjects(listing))
  }

  scan(List(), s3.listObjects(bucket, prefix))
}

要调用上述 curriedmap()函数,只需在第一个参数列表中传递已构建(并正确初始化)的 AmazonS3Client 对象(请参阅官方AWS SDK for Java API 参考)、存储桶名称和前缀名称。还传递f()要应用的函数以映射第二个参数列表中的每个对象摘要。

例如

val keyOwnerTuples = map(s3, bucket, prefix)(s => (s.getKey, s.getOwner))

将返回该(key, owner)桶/前缀中的完整元组列表

或者

map(s3, "bucket", "prefix")(s => println(s))

正如你通常在函数式编程中通过 Monads处理的那样

于 2014-06-05T12:20:53.640 回答
6

我刚刚将上面的代码更改为使用addAll而不是使用for循环来逐个添加对象,它对我有用:

List<S3ObjectSummary> keyList = new ArrayList<S3ObjectSummary>();
ObjectListing object = s3.listObjects("bucket.new.test");
keyList = object.getObjectSummaries();
object = s3.listNextBatchOfObjects(object);

while (object.isTruncated()){
  keyList.addAll(current.getObjectSummaries());
  object = s3.listNextBatchOfObjects(current);
}
keyList.addAll(object.getObjectSummaries());

之后,您可以简单地在 list keyList上使用任何迭代器。

于 2012-10-25T08:07:14.703 回答
2

如果要获取所有对象(超过 1000 个密钥),则需要将另一个带有最后一个密钥的数据包发送到 S3。这是代码。

private static String lastKey = "";
private static String preLastKey = "";
...

do{
        preLastKey = lastKey;
        AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());

        String bucketName = "bucketname";           

        ListObjectsRequest lstRQ = new ListObjectsRequest().withBucketName(bucketName).withPrefix("");  

        lstRQ.setMarker(lastKey);  

        ObjectListing objectListing = s3.listObjects(lstRQ);

        //  loop and get file on S3
        for (S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
             //   get oject and do something.....
        }
}while(lastKey != preLastKey);
于 2013-03-12T03:21:59.910 回答
2

在斯卡拉:

val first = s3.listObjects("bucket.new.test")

val listings: Seq[ObjectListing] = Iterator.iterate(Option(first))(_.flatMap(listing =>
  if (listing.isTruncated) Some(s3.listNextBatchOfObjects(listing))
  else None
))
  .takeWhile(_.nonEmpty)
  .toList
  .flatten
于 2019-07-10T08:43:24.767 回答
1
  1. Paolo Angioletti 的代码无法获取所有数据,只能获取最后一批数据。
  2. 我认为使用 ListBuffer 可能会更好。
  3. 此方法不支持设置 startAfterKey。
    import com.amazonaws.services.s3.AmazonS3Client
    import com.amazonaws.services.s3.model.{ObjectListing, S3ObjectSummary}    
    import scala.collection.JavaConverters._
    import scala.collection.mutable.ListBuffer

    def map[T](s3: AmazonS3Client, bucket: String, prefix: String)(f: (S3ObjectSummary) => T): List[T] = {

      def scan(acc: ListBuffer[T], listing: ObjectListing): List[T] = {
        val r = acc ++= listing.getObjectSummaries.asScala.map(f).toList
        if (listing.isTruncated) scan(r, s3.listNextBatchOfObjects(listing))
        else r.toList
      }

      scan(ListBuffer.empty[T], s3.listObjects(bucket, prefix))
    }

第二种方法是使用 awssdk-v2

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.1.0</version>
</dependency>
  import software.amazon.awssdk.services.s3.S3Client
  import software.amazon.awssdk.services.s3.model.{ListObjectsV2Request, S3Object}

  import scala.collection.JavaConverters._

  def listObjects[T](s3: S3Client, bucket: String,
                     prefix: String, startAfter: String)(f: (S3Object) => T): List[T] = {
    val request = ListObjectsV2Request.builder()
      .bucket(bucket).prefix(prefix)
      .startAfter(startAfter).build()

    s3.listObjectsV2Paginator(request)
      .asScala
      .flatMap(_.contents().asScala)
      .map(f)
      .toList
  }
于 2018-11-29T03:58:48.160 回答
0

默认情况下,API 最多返回 1,000 个密钥名称。响应可能包含更少的键,但永远不会包含更多。更好的实现是使用更新的 ListObjectsV2 API:

List<S3ObjectSummary> docList=new ArrayList<>();
    ListObjectsV2Request req = new ListObjectsV2Request().withBucketName(bucketName).withPrefix(folderFullPath);
    ListObjectsV2Result listing;
    do{
        listing=this.getAmazonS3Client().listObjectsV2(req);
        docList.addAll(listing.getObjectSummaries());
        String token = listing.getNextContinuationToken();
        req.setContinuationToken(token);
        LOG.info("Next Continuation Token for listing documents is :"+token);
    }while (listing.isTruncated());
于 2020-07-14T06:08:13.710 回答
0

@oferei 给出的代码效果很好,我赞成该代码。但我想指出@Abhishek 代码的根本问题。实际上,问题在于您的 do while 循环。

如果您仔细观察,您将在倒数第二条语句中获取下一批对象,然后检查您是否已经用尽了文件的总列表。因此,当您获取最后一批时, isTruncated() 变为 false 并且您跳出循环并且不处理最后 X%1000 条记录。例如:如果您总共有 2123 条记录,您最终将获取 1000 条,然后是 1000 条,即 2000 条记录。您将错过 123 条记录,因为您的 isTruncated 值会在您检查 isTruncated 值后处理下一批时中断循环。

抱歉,我不能发表评论,否则我会评论赞成的答案。

于 2020-09-24T04:16:01.977 回答