我正在尝试处理每天都在不断增长的4个文本文件目录。我需要做的是,如果有人试图搜索发票号,我应该给他们带有它的文件列表。< / p>
我能够通过将它们加载为RDD来映射和减少文本文件中的值。但是我如何获取文件名和其他文件属性?
答案 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,那么上述可能是一种有效的解决方案。不过,提出任何一个方向的贡献意见都会受到欢迎。