3

这里是 Scala 2.11,虽然这涉及AWS S3 Java 客户端 API,所以这确实是一个 Java 问题。如果有人可以在 Scala 中提供答案,那就太棒了,但我很乐意接受任何有效的 Java 答案(我总是可以在自己的时间对其进行 Scala 化)。


我正在尝试使用 AWS S3 客户端库连接到 S3 上的存储桶,该存储桶是它下面的以下目录结构:

my-bucket/
    3dj439fj9fj49j/
        data.json
    3eidi04d40d40d/
        data.json
    a874739sjsww93/
        data.json
    ...

因此,存储桶下的每个直接子对象都是具有字母数字名称的目录。我将这些称为“ID 目录”。并且这些 ID 目录中的每一个都有一个名为的子对象data.json

我需要完成几件事:

  1. 我需要一个字符串数组/映射/数据结构(JavaArray<String>或 Scala Array[String]),其中包含 ID 目录的所有字母数字 ID(因此元素 0 是"3dj439fj9fj49j",元素 1 是"3eidi04d40d40d",等等);和
  2. 我需要一个日期数组/映射/数据结构(JavaArray<Date>或 Scala Array[Date]),其中包含每个 ID 目录对应文件的Last Modified时间戳。data.json因此,如果mybucket/3dj439fj9fj49j/data.json上次修改日期/时间戳为2017-05-29 11:19:24T,那么该日期时间将是第二个数组的第一个元素
  3. 这两个数组/映射/数据结构需要是关联的,这意味着我可以访问第一个(ID)数组的第 4 个元素并获取下面的第 5 个 ID 目录my-bucket,我还可以访问第二个(日期)的第 4 个元素) 数组并获取第 5 个 ID 目录的data.json子对象的 Last Modified 时间戳

这些不一定是数组,它们可以是映射、元组等。如上所述,我只需要 1+ 个数据结构来保存此内容。

lib 的 Javadocs我看到了一个ObjectMetadata#getLastModified字段,但我没有看到任何用于读取给定父目录路径的内容S3Object(即data.json的父 ID 目录)。总而言之,我最好的尝试非常失败:

val s3Client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey))
val bucketRoot : S3Object = s3Client.getObject("myBucket","/")

// TODO: How to query 'bucketRoot' for all its child ID directories?
val idDirs : Array[S3Object] = ???

var dataMap : Map[String,Date] = null
idDirs.foreach(idDir ->
  // TODO: getName() and getChildSomehow() don't exist...obviously
  dataMap :+ idDir.getName() -> idDir.getChildSomehow("data.json").getObjectMetadata.getLastModified
)

那里有任何 S3 API 专家可以发现我哪里出错了,或者在这里将我推向正确的方向?提前致谢!

4

1 回答 1

3

您可以调用AmazonS3#listObjects(String)以获取存储桶中的对象列表。响应将包含一个S3ObjectSummary找到的每个键。您可以调用S3ObjectSummary#getLastModified()以获取最后修改的日期/时间。

这是一个将所有内容与一些 Scala 代码联系在一起的示例。

来自 S3 存储桶的输入

> aws s3 ls --recursive s3://<REDACTED>/
2017-08-02 13:45:12          0 3dj439fj9fj49j/
2017-08-02 13:45:28          0 3dj439fj9fj49j/data.json
2017-08-02 13:45:16          0 3eidi04d40d40d/
2017-08-02 13:45:33          0 3eidi04d40d40d/data.json
2017-08-02 13:45:19          0 a874739sjsww93/
2017-08-02 13:45:37          0 a874739sjsww93/data.json

代码

import collection.JavaConverters._

import com.amazonaws.auth.AWSStaticCredentialsProvider
import com.amazonaws.auth.BasicAWSCredentials
import com.amazonaws.regions.Regions
import com.amazonaws.services.s3.AmazonS3ClientBuilder

val key = <REDACTED>
val secret = <REDACTED>
val bucketName = <REDACTED>
val region = <REDACTED>

val creds = new BasicAWSCredentials(key, secret)
val s3 = AmazonS3ClientBuilder.standard.withCredentials(new AWSStaticCredentialsProvider(creds)).withRegion(region).build

val objectSummaries = s3.listObjects(bucketName).getObjectSummaries.asScala
val dataFiles = objectSummaries.filter { _.getKey.endsWith("data.json") }
val dataDirectories = dataFiles.map(dataFile => {
  val keyComponents = dataFile.getKey.split("/")
  val parent = if (keyComponents.length > 1) keyComponents(keyComponents.length - 2) else "/"
  (parent, dataFile.getLastModified)
})
dataDirectories.foreach(println)

输出

(3dj439fj9fj49j,Wed Aug 02 13:45:28 PDT 2017)
(3eidi04d40d40d,Wed Aug 02 13:45:33 PDT 2017)
(a874739sjsww93,Wed Aug 02 13:45:37 PDT 2017)

解释

首先,我们有一些引导程序来设置凭据和创建客户端。然后,我们发出listObjects,触发对 S3 服务的调用。我们filter将这些结果仅用于以“data.json”结尾的键。然后,我们map将结果转换为由父路径名和对象的最后修改日期/时间组成的元组。为了确定父路径,我们split在路径分隔符上检索前一个路径组件。作为一种特殊情况,如果文件在根目录中,那么我们说它的父级是"/".

我选择将结果表示为元组,但如果您愿意,可以将其更改为其他数据结构。

请注意,对于包含大量对象的存储桶,您可能希望改为使用AmazonS3#listObjects(String, String),以便您可以将返回的结果限制为与特定前缀匹配的键。这将减少响应消耗的网络带宽量和响应数据所需的处理量。

对于更多选项,您还可以考虑AmazonS3#listObjects(ListObjectsRequest)AmazonS3#listObjectsV2(ListObjectsV2Request)

于 2017-08-02T21:39:24.617 回答