Kotlin: 获取资源文件夹中所有文件的列表

11

有没有办法在 Kotlin 中获取“resources”文件夹中所有文件的列表?

我可以像这样读取特定文件:

Application::class.java.getResourceAsStream("/folder/filename.ext")

但有时我只想将"folder"文件夹中的所有内容提取到外部目录。

5个回答

15

因为我也曾遇到同样的问题,但找不到确切的答案,所以我不得不自己来写一个。

这是我的解决方案:

fun getAllFilesInResources()
{
    val projectDirAbsolutePath = Paths.get("").toAbsolutePath().toString()
    val resourcesPath = Paths.get(projectDirAbsolutePath, "/src/main/resources")
    val paths = Files.walk(resourcesPath)
                    .filter { item -> Files.isRegularFile(item) }
                    .filter { item -> item.toString().endsWith(".txt") }
                    .forEach { item -> println("filename: $item") }
}

我已经遍历了 /src/main/resources 文件夹中的所有文件,并仅筛选出常规文件(不包括目录),然后过滤出资源目录中的文本文件。

输出是资源文件夹中所有扩展名为.txt的绝对文件路径列表。现在您可以使用这些路径将文件复制到外部文件夹中。


我不太确定你所说的调试和jar是什么意思?你是指筛选jar文件吗?如果是这样,你只需要将.txt替换为.jar,然后你就可以筛选出所有的jar文件。 - vivek86
3
当你把东西装到罐子里时,在Java中的操作方式是不同的。我猜 Kotlin 也适用这个操作方式。 - Michael
4
在打包成JAR文件后,引用"/src/main/resources"将无法工作。 - Simon Forsberg
使用以下代码将文件写入Compose桌面应用程序的资源目录,只需要更改一行: "val projectDirAbsolutePath = Paths.get(“”)。toAbsolutePath()。toString()。plus("/ src / jvmMain / resources")" - Matt Grier
你不应该参考 /src 目录 - 这从来都不是一个好的解决方案。 - Zordid

6

两个不同的部分:

  1. 获取表示资源目录的文件
  2. 遍历目录

对于 1,可以使用Java中的 getResource

val dir = File( object {}.javaClass.getResource(directoryPath).file )

对于 2,您可以使用Kotlin的File.walk扩展函数,它返回一个序列,您可以处理其中的文件,例如:

dir.walk().forEach { f ->
    if(f.isFile) {
        println("file ${f.name}")
    } else {
        println("dir ${f.name}")
    }
}

组合在一起,您最终可能会得到以下代码:

fun onEachResource(path: String, action: (File) -> Unit) {

    fun resource2file(path: String): File {
        val resourceURL = object {}.javaClass.getResource(path)
        return File(checkNotNull(resourceURL, { "Path not found: '$path'" }).file)
    }

    with(resource2file(path)) {
        this.walk().forEach { f -> action(f) }
    }
}

这样,如果你有一个 resources/nested 目录,你可以:

fun main() {
    
    val print = { f: File ->
        when (f.isFile) {
            true -> println("[F] ${f.absolutePath}")
            false -> println("[D] ${f.absolutePath}")
        }
    }
    
    onEachResource("/nested", print)
}

5

目前没有这方面的方法(即Application::class.java.listFilesInDirectory("/folder/")),但是你可以自己创建一个系统来列出目录中的文件:

@Throws(IOException::class)
fun getResourceFiles(path: String): List<String> = getResourceAsStream(path).use{
    return if(it == null) emptyList()
    else BufferedReader(InputStreamReader(it)).readLines()
}

private fun getResourceAsStream(resource: String): InputStream? = 
        Thread.currentThread().contextClassLoader.getResourceAsStream(resource) 
                ?: resource::class.java.getResourceAsStream(resource)

只需调用getResourceFiles("/folder/"),您将获得该文件夹中的文件列表,假设它在类路径中。

这是因为Kotlin具有将行读入字符串列表的扩展函数。声明如下:

/**
 * Reads this reader content as a list of lines.
 *
 * Do not use this function for huge files.
 */
public fun Reader.readLines(): List<String> {
    val result = arrayListOf<String>()
    forEachLine { result.add(it) }
    return result
}

我觉得我的函数可以简化为以下代码:private fun getResourceList(path: String): List { val stream = Application::class.java.getResourceAsStream(path) ?: return emptyList() return stream.bufferedReader().use { it.readLines() } } - Michael
resource::class.java 总是会引用 String::class.java,这可能不是您想要的。 - Simon Forsberg
@SimonForsberg 我不知道当时我在想什么。至少现在它仍然只是一个备用方案,所以 Thread.currentThread().contextClassLoader.getResourceAsStream(resource) 至少优先于我当时想的任何东西。 - Zoe stands with Ukraine

1

这里有一个在JVM上迭代JAR打包资源的解决方案:

fun iterateResources(resourceDir: String) {
    val resource = MethodHandles.lookup().lookupClass().classLoader.getResource(resourceDir)
        ?: error("Resource $resourceDir was not found")
    FileSystems.newFileSystem(resource.toURI(), emptyMap<String, String>()).use { fs ->
        Files.walk(fs.getPath(resourceDir))
            .filter { it.extension == "ttf" }
            .forEach { file -> println(file.toUri().toString()) }
    }
}

-4

这里是我找到的一个相当简单的解决方案。

File("/path/to/file.txt")
    // make an iterable file tree
    .walk()
    // only files no directories
    .filter { it.isFile }
    // last modified from top to bottom (most recent on top)
    .sortedByDescending { it.lastModified() }
    // do things on the files
    .forEachIndexed {
        i, it ->
        // use the most recent file and delete the other ones
        if (i == 0) {
            useMe(it)
        } else {
            it.delete()
        }
    }

1
为什么要添加 .sortedByDescending { it.lastModified() }?这从未是问题的一部分。 - Simon Forsberg
5
为什么要删除其他文件?如果有人只是快速地复制黏贴这段代码而没有反思这段代码的作用,那么这会使得代码很危险(虽然在Stack Overflow上不太可能发生,对吧?) - Simon Forsberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接