使用globStatus遍历S3桶时触发URISyntaxException问题求助
问题:globStatus遍历S3桶因特殊字符目录失败,寻求替代通配方法
我原本使用globStatus结合通配符*获取S3桶内所有文件和目录,但桶内存在名为env:的含特殊字符目录,导致globStatus执行失败。
相关代码
import org.apache.hadoop.fs.{FileStatus, FileSystem, Path} val uri = validatePath("Bucket_path") val fs = getFs(uri) val fileStatuses = fs.globStatus(new Path(uri))
报错堆栈
Caused by: java.lang.IllegalArgumentException: java.net.URISyntaxException: Expected scheme-specific part at index 4: env: at org.apache.hadoop.fs.Path.initialize(Path.java:263) at org.apache.hadoop.fs.Path.<init>(Path.java:221) at org.apache.hadoop.fs.Path.<init>(Path.java:129) at org.apache.hadoop.fs.Globber.doGlob(Globber.java:338) at org.apache.hadoop.fs.Globber.glob(Globber.java:202) at org.apache.hadoop.fs.s3a.S3AFileSystem.globStatus(S3AFileSystem.java:4253) at org.apache.hadoop.fs.s3a.S3AFileSystem.globStatus(S3AFileSystem.java:4233) ... 40 more Caused by: java.net.URISyntaxException: Expected scheme-specific part at index 4: env: at java.net.URI$Parser.fail(URI.java:2847) at java.net.URI$Parser.failExpecting(URI.java:2853) at java.net.URI$Parser.parse(URI.java:3056) at java.net.URI.<init>(URI.java:746) at org.apache.hadoop.fs.Path.initialize(Path.java:260) ... 50 more
补充说明
该问题在Hadoop Common的Jira工单中处于开放状态。
替代解决方案
1. 用listStatus遍历后手动过滤
放弃globStatus的自动通配,直接调用listStatus获取根目录下所有条目,再手动模拟通配规则筛选:
val rootPath = new Path(uri) val allStatuses = fs.listStatus(rootPath) // 模拟*通配:保留所有非隐藏的文件/目录 val filteredStatuses = allStatuses.filter(status => { val entryName = status.getPath.getName !entryName.startsWith(".") })
如果需要更复杂的通配逻辑,可借助Hadoop的GlobPattern类做匹配:
import org.apache.hadoop.fs.GlobPattern val globPattern = new GlobPattern("*") val filteredStatuses = allStatuses.filter(status => { globPattern.matches(status.getPath.getName) })
2. 临时转义特殊字符
针对env:这类被误判为URI协议的目录名,尝试对特殊字符进行URL转义(注意:该方法兼容性依赖Hadoop版本,仅作为临时 workaround):
// 转义冒号前的字符,避免被解析为URI scheme val escapedUri = uri.replace("env:", "%65nv:") val fileStatuses = fs.globStatus(new Path(escapedUri))
3. 改用S3原生SDK操作
绕开Hadoop FS API,直接使用AWS S3 SDK列出桶内对象,自行处理通配逻辑:
import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.ListObjectsV2Request val s3Client = S3Client.create() val listRequest = ListObjectsV2Request.builder() .bucket("your-bucket-name") // 替换为实际桶名 .prefix("") // 空前缀表示遍历根目录 .build() val response = s3Client.listObjectsV2(listRequest) // 过滤符合*通配的条目 val filteredEntries = response.contents().filter(obj => { val keyParts = obj.key().split("/") keyParts.headOption.forall(name => !name.startsWith(".")) })
内容的提问来源于stack exchange,提问作者PseudoAccount
相关产品推荐
相关产品推荐

