Amazon s3 возвращает только 1000 записей для одного ведра и все для другого ведра (используя java sdk)?

Я использую приведенный ниже код, чтобы получить список всех имен файлов из корзины s3. У меня два ведра в с3. Для одного из сегментов ниже код возвращает все имена файлов (более 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());

Ответ 1

Улучшение @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());
}

Ответ 2

Для разработчиков Scala здесь рекурсивная функция выполняет полное сканирование и отображение содержимого ведра AmazonS3 с использованием официального AWS SDK для Java

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

Чтобы вызвать вышеупомянутую функцию curried map(), просто передайте уже построенный (и правильно инициализированный) объект AmazonS3Client (обратитесь к официальному AWS SDK для Java API Ссылка), имя ведра и имя префикса в списке первых параметров. Также передайте функцию f(), которую вы хотите применить, для сопоставления всех сводок объектов во втором списке параметров.

Например

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

вернет полный список кортежей (key, owner) в этом ведре/префиксе

или

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

как вы обычно подходите Monads в функциональном программировании

Ответ 3

Я только что изменил код выше, чтобы использовать 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());

После этого вы можете просто использовать любой итератор над списком ключей.

Ответ 4

Если вы хотите получить весь объект (более 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);

Ответ 5

  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
  }

Ответ 6

В 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