如何列出Amazon S3目录下Parquet文件名?遇SSL连接类方法缺失报错
我来帮你解决这个问题——你遇到的java.lang.NoSuchMethodError本质是依赖版本冲突,而列出Parquet文件的代码也可以优化一下,下面一步步拆解解决方案:
首先分析错误原因
NoSuchMethodError几乎都是因为类路径里同时存在多个版本的同一依赖,导致JVM加载的类和代码预期的方法不匹配。在你的场景里,问题出在org.apache.httpcomponents相关的库(httpclient/httpcore):Spark、Hadoop AWS、AWS SDK甚至你引入的jets3t都可能自带不同版本的http组件,和你手动添加的4.5.2版本冲突了。
解决依赖冲突的步骤
1. 先排查依赖树
用SBT的dependencyTree命令查看所有依赖的版本,找出哪些库引入了冲突的http组件:
sbt dependencyTree
重点看org.apache.httpcomponents下的httpclient和httpcore,标记出所有非4.5.2的版本来源。
2. 调整依赖配置,统一版本并排除冲突
修改你的SBT依赖,把所有可能引入冲突http组件的依赖都排除掉,然后统一引入匹配的版本:
libraryDependencies ++= Seq( "org.apache.spark" %% "spark-core" % sparkVersion excludeAll( ExclusionRule("commons-httpclient", "commons-httpclient"), ExclusionRule("org.apache.httpcomponents", "httpclient"), ExclusionRule("org.apache.httpcomponents", "httpcore") ), "org.apache.spark" %% "spark-mllib" % sparkVersion excludeAll( ExclusionRule("commons-httpclient", "commons-httpclient"), ExclusionRule("org.apache.httpcomponents", "httpclient"), ExclusionRule("org.apache.httpcomponents", "httpcore") ), "org.sedis" %% "sedis" % "1.2.2", "org.scalactic" %% "scalactic" % "3.0.0", "org.scalatest" %% "scalatest" % "3.0.0" % "test", "com.github.nscala-time" %% "nscala-time" % "2.14.0", "com.amazonaws" % "aws-java-sdk-s3" % "1.11.53" excludeAll( ExclusionRule("org.apache.httpcomponents", "httpclient"), ExclusionRule("org.apache.httpcomponents", "httpcore") ), // 统一引入匹配版本的http组件 "org.apache.httpcomponents" % "httpclient" % "4.5.2", "org.apache.httpcomponents" % "httpcore" % "4.4.4", // 和httpclient 4.5.2严格匹配 "org.apache.hadoop" % "hadoop-aws" % "2.6.0" excludeAll( ExclusionRule("commons-httpclient", "commons-httpclient"), ExclusionRule("org.apache.httpcomponents", "httpclient"), ExclusionRule("org.apache.httpcomponents", "httpcore") ), "com.github.scopt" %% "scopt" % "3.3.0" )
另外,如果你没有用到net.java.dev.jets3t,建议直接移除这个依赖——它依赖旧版本的commons-httpclient,很容易引发冲突。
优化S3 Parquet文件遍历代码
你的现有代码可以拿到所有S3对象,但需要过滤出Parquet文件,还要注意前缀的写法(避免匹配到类似directoryXYZ的路径),修改后的代码:
import com.amazonaws.services.s3.{AmazonS3, AmazonS3ClientBuilder} import com.amazonaws.services.s3.model.S3ObjectSummary import scala.jdk.CollectionConverters._ // Scala 2.13+;Scala 2.12及以下用JavaConverters._ val s3: AmazonS3 = AmazonS3ClientBuilder.standard().build() val bucketName = "bucketname" val prefix = "directory/" // 末尾加斜杠,确保只匹配该目录下的对象 var objs = s3.listObjects(bucketName, prefix) val summaries = new java.util.ArrayList[S3ObjectSummary](objs.getObjectSummaries()) // 处理分页结果 while (objs.isTruncated()) { objs = s3.listNextBatchOfObjects(objs) summaries.addAll(objs.getObjectSummaries()) } // 过滤出Parquet文件,并提取文件名(去掉前缀) val listOfParquetFiles = summaries.asScala .filter(_.getKey.endsWith(".parquet")) .map(summary => summary.getKey.substring(prefix.length)) .toArray
更简洁的替代方案:用Spark API直接获取
既然你已经在使用Spark,其实可以直接用Spark的内置API读取S3路径,不需要手动调用AWS SDK,还能避免依赖冲突:
import org.apache.spark.sql.SparkSession val spark = SparkSession.builder().getOrCreate() val parquetS3Path = "s3a://bucketname/directory/" // 读取Parquet文件 val parquetDF = spark.read.parquet(parquetS3Path) // 获取所有Parquet文件的完整路径 val parquetFilePaths = parquetDF.inputFiles // 如果只需要文件名,拆分路径即可 val parquetFileNames = parquetFilePaths.map(_.split("/").last)
内容的提问来源于stack exchange,提问作者Markus

