Spark-获取RDD中的文件名

时间:2015-04-16 21:48:34

标签: apache-spark

我正在尝试处理每天都在不断增长的4个文本文件目录。我需要做的是,如果有人试图搜索发票号,我应该给他们带有它的文件列表。< / p>

我能够通过将它们加载为RDD来映射和减少文本文件中的值。但是我如何获取文件名和其他文件属性?

7 个答案:

答案 0 :(得分:20)

自Spark 1.6起,您可以将text数据源和input_file_name函数组合如下:

<强> Scala的

import org.apache.spark.sql.functions.input_file_name

val inputPath: String = ???

spark.read.text(inputPath)
  .select(input_file_name, $"value")
  .as[(String, String)] // Optionally convert to Dataset
  .rdd // or RDD

<强>的Python

2.x之前的版本有错误,转换为RDD后可能无法保留名称):

from pyspark.sql.functions import input_file_name

(spark.read.text(input_path)
    .select(input_file_name(), "value"))
    .rdd)

这也可以与其他输入格式一起使用。

答案 1 :(得分:4)

如果您的文字文件足够小,可以使用SparkContext.wholeTextFiles返回(filename,content)的RDD。

答案 2 :(得分:3)

如果您的文字文件对于SparkContext.wholeTextFiles来说太大,您可以使用(简单)自定义InputFormat然后调用SparkContext.hadoopRDD

InputFormat需要返回一个元组(文件名,行)而不是行,然后您可以使用查看该行内容的谓词进行过滤,然后将其唯一并收集文件名。

从Spark中,代码看起来像:

val ft = classOf[FileNamerInputFormat]
val kt = classOf[String]
val vt = classOf[String]

val hadoopConfig = new Configuration(sc.hadoopConfiguration)
sc.newAPIHadoopFile(path, ft, kt, vt, hadoopConfig)
  .filter { case (f, l) => isInteresting(l) }
  .map { case (f, _) => f } 
  .distinct()
  .collect()

答案 3 :(得分:3)

如果你在pyspark:

,你可以试试这个
    test = sc.wholeTextFiles("pathtofile")

您将获得第一个元素=文件路径和第二个元素=内容

的结果RDD

答案 4 :(得分:3)

您可以使用WholeTextFile()来实现此目的。但是,如果输入文件很大,那么使用WholeTextFile()会产生反效果,因为它将整个文件内容放入一个记录中。

在这种情况下检索文件名的最佳方法是使用mapPartitionsWithInputSplit()。您可以在my blog上找到使用此方案的工作示例。

答案 5 :(得分:2)

如果您正在使用数据框API,则可以使用input_file_name中的org.apache.spark.sql.functions函数从HDFS获取文件名。下面的代码片段可能会帮助您理解。

val df = spark.read.csv("/files/")
val df2 = df.withColumn("file_name", split(input_file_name(), "/").getItem(7).cast(StringType)) 
val df3 = df.withColumn("file_name", input_file_name()) 

df2现在包括一个名为“ file_name”的新字段,其中包含使用split函数提取的HDFS文件名。如果需要完整的HDFS路径,则只能使用input_file_name()中所示的df3函数。

答案 6 :(得分:1)

直接使用Spark似乎有点过分......如果要收集这些数据&#39;对于驱动程序,为什么不使用HDFS API? Hadoop通常与Spark捆绑在一起。这是一个例子:

import org.apache.hadoop.fs._
import org.apache.hadoop.conf._

val fileSpec = "/data/Invoices/20171123/21"
val conf = new Configuration()
val fs = org.apache.hadoop.fs.FileSystem.get(new URI("hdfs://nameNodeEneteredHere"),conf)
val path = new Path(fileSpec)
// if(fs.exists(path) && fs.isDirectory(path) == true) ...
val fileList = fs.listStatus(path)

然后使用println(fileList(0)),将第一项(作为示例)的信息(格式化)视为org.apache.hadoop.fs.FileStatus

FileStatus {
    path=hdfs://nameNodeEneteredHere/Invoices-0001.avro; 
    isDirectory=false; 
    length=29665563;
    replication=3;
    blocksize=134217728;
    modification_time=1511810355666;
    access_time=1511838291440;
    owner=codeaperature;
    group=supergroup;
    permission=rw-r--r--;
    isSymlink=false
}

fileList(0).getPath将提供hdfs://nameNodeEneteredHere/Invoices-0001.avro

我想这种读取文件的方法主要是使用HDFS名称节点而不是每个执行程序。 TLDR;我猜赌Spark可能会调查namenode来获取RDD。如果基础Spark调用轮询namenode来管理RDD,那么上述可能是一种有效的解决方案。不过,提出任何一个方向的贡献意见都会受到欢迎。