Spark Scala实现本地图片读取并写入HDFS报错求助
解决Spark读取二进制图片并写入HDFS的问题
你的问题出在用文本处理的方式来处理二进制图片文件上,这是Spark新手常踩的坑,我来帮你拆解原因和解决办法:
问题根源
wholeTextFiles API的设计目的是读取文本文件,它会把文件内容当作字符串(String类型)加载到Spark中。但图片是二进制数据,强行用文本方式读取会导致数据解码错误(比如把二进制字节当成UTF-8字符解析,出现乱码或丢失字节),后续用write.text()写入时,又会把损坏的字符串再编码,最终不仅图片无法还原,还会因为非法字符触发写入异常。
正确解决方案
我们需要用Spark专门处理二进制文件的API来读取,再用合适的格式写入HDFS:
1. 用二进制方式读取图片
Spark 2.0+提供了binaryFile格式的数据源,可以直接读取文件的路径和二进制内容,完全保留图片的原始数据:
// 读取本地图片目录,返回包含文件元数据和二进制内容的DataFrame val imageDF = spark.read.format("binaryFile") .option("pathGlobFilter", "*.jpg,*.png") // 可选:过滤特定后缀的图片 .load("file:///home/jeffi/input/Images_Test/") // 查看Schema,确认数据结构 imageDF.printSchema() // root // |-- path: string (nullable = true) // |-- modificationTime: timestamp (nullable = true) // |-- length: long (nullable = true) // |-- content: binary (nullable = true)
2. 写入HDFS的正确方式
因为是二进制数据,不能用write.text()(文本格式只支持字符串),推荐两种方案:
方案一:用Parquet格式存储(推荐)
Parquet是Spark默认的列式存储格式,原生支持二进制类型,能高效存储和读取图片数据,还保留文件元信息:
// 写入HDFS的Parquet目录,替换为你的HDFS路径 imageDF.write.mode("overwrite").parquet("hdfs://your-hdfs-host:port/images-storage")
方案二:还原为单独的图片文件
如果需要把每个图片还原成HDFS上的独立文件,可以结合HDFS API来实现:
import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.spark.sql.Row // 获取HDFS文件系统实例 val hdfsFs = FileSystem.get(spark.sparkContext.hadoopConfiguration) // 遍历每一行数据,将二进制内容写入HDFS的对应文件 imageDF.foreach { row => val originalPath = new Path(row.getAs[String]("path")) val imageContent = row.getAs[Array[Byte]]("content") // 构造HDFS输出路径,保留原文件名 val outputPath = new Path("hdfs://your-hdfs-host:port/images-output/" + originalPath.getName) // 写入文件 val outputStream = hdfsFs.create(outputPath) outputStream.write(imageContent) outputStream.close() }
验证结果
- 用Parquet存储的话,后续可以用
spark.read.parquet()读取,直接获取二进制内容还原图片; - 用方案二写入的话,可以直接在HDFS上用
hadoop fs -cat查看(虽然图片是二进制看不到内容,但文件大小和原文件一致就说明成功了)。
内容的提问来源于stack exchange,提问作者Teju Priya
相关产品推荐
相关产品推荐

