【问题标题】:How to list files in S3 bucket using Spark Session?如何使用 Spark Session 列出 S3 存储桶中的文件?
【发布时间】:2019-06-01 06:49:19
【问题描述】:

是否可以使用 SparkSession 对象列出给定 S3 路径(例如:s3://my-bucket/my-folder/*.extension)中的所有文件?

【问题讨论】:

  • 您试图实现的用例是什么?
  • 我知道使用 AWS S3 SDK API 可以做到这一点,但想知道 SparkSession 对象是否支持它。我有兴趣计算特定 S3 路径中有多少文件包含特定文件格式(例如:*.extension),并且还想知道它的完整路径/文件名。

标签: apache-spark amazon-s3 apache-spark-sql


【解决方案1】:

您可以将input_file_name 与数据框一起使用,它会为您提供每行的绝对文件路径。

以下代码将为您提供所有文件路径。

spark.read.table("zen.intent_master").select(input_file_name).distinct.collect

我假设。对于您的用例,您只想使用一些正则表达式从一组文件中读取数据,然后您可以在过滤器中应用它。

例如,

val df = spark.read.table("zen.intent_master").filter(input_file_name.rlike("your regex string"))

【讨论】:

  • 哦,对于这个特定的问题,它没有或不使用表格。如果没有桌子可以这样做吗?我只能访问 S3 文件夹
  • @codeshark 你是对的,但我现在没有 S3 设置写入,我已经用 HDFS 进行了测试,它工作正常,我假设它也适用于 s3。
  • 您也可以将其与 s3 一起使用,例如:val blah = spark.read.json("s3://blah-bucket/blah-folder/") val blahWithFilename = blah.withColumn("filename", input_file_name) blahWithFilename.show(false)
【解决方案2】:

您可以使用 Hadoop API 访问 S3 上的文件(Spark 也使用它):

import java.net.URI
import org.apache.hadoop.fs.FileSystem
import org.apache.hadoop.fs.Path
import org.apache.hadoop.conf.Configuration

val path = "s3://somebucket/somefolder"
val fileSystem = FileSystem.get(URI.create(path), new Configuration())
val it = fileSystem.listFiles(new Path(path), true)
while (it.hasNext()) {
  ...
}

【讨论】:

  • 这是这样做的方法,并且 listFiles(path, true) 可以为您提供比尝试自己进行 treewalk 更好的 s3 性能。
  • 我们如何从这个迭代器中提取文件列表并将其存储在一个数组中?我在这个链接docs.scala-lang.org/overviews/collections/iterators.html 中看到许多函数应该可用于迭代器;但是,不能激活除 .hasNext() 和 .next() 之外的任何一个
  • 这对我根本不起作用。我有很多关于 s3 的错误。我不记得它们到底是什么,但我尝试了很多不同的方法。只是评论如果其他人遇到同样的事情,他们不会认为自己疯了。
  • @Mahmoud itorg.apache.hadoop.fs.RemoteIterator 的一个实例,而不是集合的迭代器
  • 这对我有用。我需要在 while 循环之外定义一个 var 以保存我要查找的文件名的结果。另外,为了在找到我的文件后停止循环,我需要将 while 循环包装在 breakable 中,然后在找到文件后我可以 break
【解决方案3】:

方法 1

对于 pyspark 用户,我已经翻译了 Michael Spector 的 answer(由您决定使用它是否是一个好主意):

sc = spark.sparkContext
myPath = f's3://my-bucket/my-prefix/'
javaPath = sc._jvm.java.net.URI.create(myPath)
hadoopPath = sc._jvm.org.apache.hadoop.fs.Path(myPath)
hadoopFileSystem = sc._jvm.org.apache.hadoop.fs.FileSystem.get(javaPath, sc._jvm.org.apache.hadoop.conf.Configuration())
iterator = hadoopFileSystem.listFiles(hadoopPath, True)

s3_keys = []
while iterator.hasNext():
    s3_keys.append(iterator.next().getPath().toUri().getRawPath())    

s3_keys 现在保存在my-bucket/my-prefix 找到的所有文件密钥

方法 2 这是我发现的另一种选择(hat tip 到 @forgetso):

myPath = 's3://my-bucket/my-prefix/*'
hadoopPath = sc._jvm.org.apache.hadoop.fs.Path(myPath)
hadoopFs = hadoopPath.getFileSystem(sc._jvm.org.apache.hadoop.conf.Configuration())
statuses = hadoopFs.globStatus(hadoopPath)

for status in statuses:
  status.getPath().toUri().getRawPath()
  # Alternatively, you can get file names only with:
  # status.getPath().getName()

方法 3(不完整!)

上述两种方法不使用将应用于分布式读取的 Spark 并行机制。不过,这种逻辑看起来很私密。见parallelListLeafFileshere。我还没有找到一种方法来强制 pyspark 在 s3 上分发 ls 而不读取文件内容。我尝试使用 py4j 实例化 InMemoryFileIndex,但无法正确使用咒语。如果有人想从这里取走,这是我目前所拥有的:

myPath = f's3://my-bucket/my-path/'
paths = sc._gateway.new_array(sc._jvm.org.apache.hadoop.fs.Path, 1)
paths[0] = sc._jvm.org.apache.hadoop.fs.Path(myPath)

emptyHashMap = sc._jvm.java.util.HashMap()
emptyScalaMap = sc._jvm.scala.collection.JavaConversions.mapAsScalaMap(emptyMap)

# Py4J is not happy with this:
sc._jvm.org.apache.spark.sql.execution.datasources.InMemoryFileIndex(
    spark._jsparkSession, 
    paths, 
    emptyScalaMap, 
    sc._jvm.scala.Option.empty() # Optional None
)

【讨论】:

  • Apache Hadoop 发行版(不是 EMR)中的 FWIW S3a 对结果中的下一页进行异步预取。整个“并行与否”问题是一个有趣的问题。它可以在单个主机上有效地完成,但是您必须在并行的浅 LIST 调用与深度顺序 LIST 之间做出决定……实际上取决于目录结构,哪个是最好的。我目前的策略是列出直接的孩子,然后在单独的线程中的每个孩子下的深度列表。
猜你喜欢
  • 2020-01-24
  • 2023-01-20
  • 1970-01-01
  • 1970-01-01
  • 2019-12-02
  • 2021-09-29
  • 2017-12-07
  • 2016-03-05
  • 2013-01-17
相关资源
最近更新 更多