我发现在资源文件夹中列出文件的唯一选项是使用 nio 的文件系统概念,因为这可以将 jar 文件作为文件系统加载。但这有两个主要缺点:
- java.nio 使用 java Stream API,我无法从 scala 代码中收集它:
Collectors.toList() 无法编译,因为它无法确定正确的类型。
- 对于 OS 文件系统和基于 jar 文件的文件系统,文件系统需要不同的基本路径。所以我需要手动区分测试和基于jar的运行两种情况。
如果需要,首先延迟加载 jar 文件系统
private static FileSystem jarFileSystem;
static synchronized private FileSystem getJarFileAsFilesystem(String drg_file_root) throws URISyntaxException, IOException {
if (jarFileSystem == null) {
jarFileSystem = FileSystems.newFileSystem(ConfigFiles.class.getResource(drg_file_root).toURI(), Collections.emptyMap());
}
return jarFileSystem;
}
接下来通过检查 URL 的协议并返回 Path 来确定我们是否在 jar 中。 (jar 文件中的协议为jar:
static Path getPathForResource(String resourceFolder, String filename) throws IOException, URISyntaxException {
URL url = ConfigFiles.class.getResource(resourceFolder + "/" + filename);
return "file".equals(url.getProtocol())
? Paths.get(url.toURI())
: getJarFileAsFilesystem(resourceFolder).getPath(resourceFolder, filename);
}
最后列出并收集到一个java列表中
static List<Path> listPathsFromResource(String resourceFolder, String subFolder) throws IOException, URISyntaxException {
return Files.list(getPathForResource(resourceFolder, subFolder))
.filter(Files::isRegularFile)
.sorted()
.collect(toList());
}
只有这样我们才能回去做 Scala 和 fetch
class SpecReader {
def readSpecMessage(spec: String): String = {
List("CN", "DO", "KF")
.flatMap(ConfigFiles.listPathsFromResource(s"/spec_$spec", _).asScala.toSeq)
.flatMap(path ⇒ Source.fromInputStream(Files.newInputStream(path), "UTF-8").getLines())
.reduce(_ + " " + _)
}
}
object Main {
def main(args: Array[String]): Unit = {
System.out.println(new SpecReader().readSpecMessage(args.head))
}
}
我在这里放了一个正在运行的迷你项目来证明:https://github.com/kurellajunior/list-files-from-resource-directory
但这当然远非最佳。我想消除上面提到的两个缺点,所以,
- 仅限 scala 文件
- 我的生产库中没有额外的测试代码