Databricks打包JAR运行触发FileInfo类转换异常如何解决?
异常原因
这个异常并非Databricks的BUG,是本地与集群运行环境的DBUtils实现差异导致的:
- 本地Databricks Connect环境的
DBUtils属于com.databricks.service包,DBUtils.fs.ls返回的FileInfo和你代码导入的类完全匹配,因此运行正常。 - 代码打包上传到Databricks集群运行时,集群原生的
DBUtils.fs.ls实际返回的是com.databricks.backend.daemon.dbutils.FileInfo类实例,和你导入的service包下的FileInfo不属于同一个类,类加载器无法完成转换,因此抛出异常。
修复方案
下面提供三个可落地的修复方式,可根据你的项目情况选择:
- 方案1:调整依赖scope,打包时排除DBUtils相关依赖
在构建配置中将dbutils-api的依赖标记为provided,让代码运行时优先加载集群环境提供的原生DBUtils实现,不需要修改业务代码即可解决问题。
sbt配置示例:
libraryDependencies += "com.databricks" % "dbutils-api_2.12" % "0.0.6" % Provided
Maven配置示例:
<dependency> <groupId>com.databricks</groupId> <artifactId>dbutils-api_2.12</artifactId> <version>0.0.6</version> <scope>provided</scope> </dependency>
- 方案2:代码中通过反射读取属性,规避类转换问题
如果不想调整打包逻辑,可以修改getAllFiles方法,不直接强类型引用FileInfo类,通过反射读取path属性:
import com.databricks.service.{DBUtils, FileInfo} ... def getAllFiles(path: String): Seq[String] = { val files = DBUtils.fs.ls(path) if (files.isEmpty) List() else files.map(file => { // 反射调用path方法,适配不同包下的FileInfo实现 val path: String = file.getClass.getMethod("path").invoke(file).toString if (DBUtils.fs.dbfs.getFileStatus(new org.apache.hadoop.fs.Path(path)).isDirectory) getAllFiles(path) else List(path) }).reduce(_ ++ _) }
- 方案3:改用Hadoop FileSystem API遍历目录,完全绕过DBUtils兼容问题
如果需要代码在更多环境下都能正常运行,可以直接使用Hadoop标准的文件系统API遍历目录,不需要依赖DBUtils:
import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.spark.SparkContext def getAllFiles(path: String, sc: SparkContext): Seq[String] = { val fs = FileSystem.get(sc.hadoopConfiguration) val statuses = fs.listStatus(new Path(path)) if (statuses.isEmpty) { List() } else { statuses.map(status => { val currPath = status.getPath.toString if (status.isDirectory) getAllFiles(currPath, sc) else List(currPath) }).reduce(_ ++ _) } }
内容的提问来源于stack exchange,提问作者marknorkin
相关产品推荐
相关产品推荐

