通过Spark SQL DataFrame.write()方法生成的输出文件以" part"开头。 basename前缀。 e.g。
DataFrame sample_07 = hiveContext.table("sample_07");
sample_07.write().parquet("sample_07_parquet");
结果:
hdfs dfs -ls sample_07_parquet/
Found 4 items
-rw-r--r-- 1 rob rob 0 2016-03-19 16:40 sample_07_parquet/_SUCCESS
-rw-r--r-- 1 rob rob 491 2016-03-19 16:40 sample_07_parquet/_common_metadata
-rw-r--r-- 1 rob rob 1025 2016-03-19 16:40 sample_07_parquet/_metadata
-rw-r--r-- 1 rob rob 17194 2016-03-19 16:40 sample_07_parquet/part-r-00000-cefb2ac6-9f44-4ce4-93d9-8e7de3f2cb92.gz.parquet
我想更改使用Spark SQL DataFrame.write()创建文件时使用的输出文件名前缀。我尝试设置" mapreduce.output.basename" Spark上下文的hadoop配置的属性。 e.g。
public class MyJavaSparkSQL {
public static void main(String[] args) throws Exception {
SparkConf sparkConf = new SparkConf().setAppName("MyJavaSparkSQL");
JavaSparkContext ctx = new JavaSparkContext(sparkConf);
ctx.hadoopConfiguration().set("mapreduce.output.basename", "myprefix");
HiveContext hiveContext = new org.apache.spark.sql.hive.HiveContext(ctx.sc());
DataFrame sample_07 = hiveContext.table("sample_07");
sample_07.write().parquet("sample_07_parquet");
ctx.stop();
}
这没有改变生成文件的输出文件名前缀。
有没有办法在使用DataFrame.write()方法时覆盖输出文件名前缀?
答案 0 :(得分:7)
使用任何标准输出格式(如Parquet)时,无法更改“part”前缀。请参阅ParquetRelation source code中的此片段:
private val recordWriter: RecordWriter[Void, InternalRow] = {
val outputFormat = {
new ParquetOutputFormat[InternalRow]() {
// ...
override def getDefaultWorkFile(context: TaskAttemptContext, extension: String): Path = {
// ..
// prefix is hard-coded here:
new Path(path, f"part-r-$split%05d-$uniqueWriteJobId$bucketString$extension")
}
}
}
如果你真的必须控制零件文件名,你可能必须实现一个自定义FileOutputFormat并使用一个接受FileOutputFormat类的Spark的保存方法(例如saveAsHadoopFile)。
答案 1 :(得分:0)
假设输出文件夹中仅包含一个csv
文件,我们可以使用以下代码对其进行语法化(动态)重命名。在下面的代码(最后一行)中,使用csv
类型从输出目录中获取所有文件,并将其重命名为所需的文件名。
import org.apache.hadoop.fs.{FileSystem, Path}
import org.apache.hadoop.conf.Configuration
val outputfolder_Path = "s3://<s3_AccessKey>:<s3_Securitykey>@<external_bucket>/<path>"
val fs = FileSystem.get(new java.net.URI(outputfolder_Path), new Configuration())
fs.globStatus(new Path(outputfolder_Path + "/*.*")).filter(_.getPath.toString.split("/").last.split("\\.").last == "csv").foreach{l=>{ fs.rename(new Path(l.getPath.toString), new Path(outputfolder_Path + "/DesiredFilename.csv")) }}
答案 2 :(得分:0)
同意@Tzach Zohar..
将数据帧保存到 HDFS 或 S3 后,您可以使用以下重命名...
下面的 scala 示例已经准备就绪 :-) 意味着您可以直接在您的代码或 util 中使用 写入 HDFS 或 S3 后,您可以使用以下定义重命名文件..
#简要:
1) 使用 globstatus 获取文件夹下的所有文件。
2)循环并使用前缀或后缀重命名文件。
注意:Apache Commons 已经在 hadoop 集群中可用,所以不需要任何进一步的依赖。
/**
* prefixHdfsFiles
* @param outputfolder_Path
* @param prefix
*/
def prefixHdfsFiles(outputfolder_Path: String, prefix: String) = {
import org.apache.hadoop.fs.{_}
import org.apache.hadoop.conf.Configuration
import org.apache.commons.io.FilenameUtils._
import java.io.File
import java.net.URI
val fs = FileSystem.get(new URI(outputfolder_Path), new Configuration())
fs.globStatus(
new Path(outputfolder_Path + "/*.*")).foreach { l: FileStatus => {
val newhdfsfileName = new Path(getFullPathNoEndSeparator(l.getPath.toString) + File.separatorChar + prefix + getName(l.getPath.toString))
// fs.rename(new Path(l.getPath.toString),newhdfsfileName )
val change = s"""
|original ${ new Path(l.getPath.toString) } --> new $newhdfsfileName
|""".stripMargin
println( change)
}
}
}
来电者例如:
val outputfolder_Path = "/a/b/c/d/e/f/"
prefixHdfsFiles(outputfolder_Path, "myprefix_")