亚马逊s3只为一个存储桶返回1000个条目,而为另一个存储桶返回所有条目(使用java sdk)?

时间:2012-10-12 06:30:55

标签: java amazon-s3

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

我的存储桶也有层次结构文件夹/ filename.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());

8 个答案:

答案 0 :(得分:9)

改善@ 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());
}

答案 1 :(得分:7)

对于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))
}

要调用上面的curry map()函数,只需在第一个参数中传递已构造的(并且已正确初始化的)AmazonS3Client对象(请参阅官方AWS SDK for Java API Reference),存储桶名称和前缀名称名单。同时传递要应用的函数f()以映射第二个参数列表中的每个对象摘要。

例如

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

将返回该存储桶/前缀

(key, owner)个元组的完整列表

map(s3, "bucket", "prefix")(s => println(s))
通常按Monads in Functional Programming

进行操作

答案 2 :(得分:5)

我刚刚更改了上面的代码,使用 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());

之后,您只需使用列表 keyList 上的任何迭代器。

答案 3 :(得分: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);

答案 4 :(得分:0)

  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
  }

答案 5 :(得分:0)

在Scala中:

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

答案 6 :(得分: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());

答案 7 :(得分:0)

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

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

抱歉,我无法发表评论,否则我会对已投票的答案发表评论。