我有一个用Kotlin目标框架30+编写的Android应用程序,所以我在新的Android 11 file access restrictions中工作。该应用程序需要能够打开共享存储中的任意.zip文件(由用户交互选择),然后处理该.zip文件的内容。
我正在获取.zip文件的URI,我被引导理解为规范的方式:
val activity = this
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) {
CoroutineScope(Dispatchers.Main).launch {
if(it != null) doStuffWithZip(activity, it)
...
}
}
getContent.launch("application/zip")
我的问题是,我使用的Java.util.zip.ZipFile class只知道如何打开由String或File指定的.zip文件,而我没有任何简单的方法从Uri访问这两个文件(我猜ZipFile对象需要实际的文件而不是某种流,因为它需要能够查找...)。
我目前使用的解决方法是将URI转换为InputStream,将内容复制到私有存储中的临时文件,并从中创建一个ZipFile示例:
private suspend fun <T> withZipFromUri(
context: Context,
uri: Uri, block: suspend (ZipFile) -> T
) : T {
val file = File(context.filesDir, "tempzip.zip")
try {
return withContext(Dispatchers.IO) {
kotlin.runCatching {
context.contentResolver.openInputStream(uri).use { input ->
if (input == null) throw FileNotFoundException("openInputStream failed")
file.outputStream().use { input.copyTo(it) }
}
ZipFile(file, ZipFile.OPEN_READ).use { block.invoke(it) }
}.getOrThrow()
}
} finally {
file.delete()
}
}
然后,我可以这样使用它:
suspend fun doStuffWithZip(context: Context, uri: Uri) {
withZipFromUri(context, uri) { // it: ZipFile
for (entry in it.entries()) {
dbg("entry: ${entry.name}") // or whatever
}
}
}
这是可行的,而且(在我的特定情况下,所讨论的.zip文件永远不会超过几MB)性能相当好。
但是,我倾向于将通过临时文件编程视为无能者的最后避难所,因此我无法逃避我错过了一个技巧的感觉。(诚然,在Android +Kotlin的环境中,我 * 是 * 无能者,但我想学会不...)
有更好的主意吗?有没有一种更干净的方法来实现这个,而不涉及到额外的文件副本?
5条答案
按热度按时间nbysray51#
从外部资源复制(并冒着被遗忘的风险),这不是一个很好的答案,但太长的评论
这显然适用于预先存在的文件;但是,由于您已经有一个从
Uri
读取的输入流-您可以调整这个并给予一下。编辑:看起来它也在解压缩到
File
中--你可以将各个ByteArrays存储在某个地方,然后决定以后如何处理它们。但是我希望你能得到一个总体的想法--你可以在内存中完成所有这些工作,而不必在中间使用磁盘(临时文件或文件)。但是,您的要求有点模糊不清,所以我不知道您想做什么,只是建议了一个尝试的地点/方法
6yoyoihd2#
简单的ZipInputStream怎么样?- Shark
好主意@鲨鱼。
ycggw6v23#
@Shark在ZipInputStream上有它。我不知道我一开始是怎么错过的,但我确实错过了。
我的
withZipFromUri()
方法现在更简单更好了:这与旧的调用不兼容(因为块函数现在接受
ZipInputStream
作为参数,而不是ZipFile
)。在我的特定情况下--实际上,在任何情况下,只要使用者不介意按照条目出现的顺序处理条目--这就可以了。sbdsn5lh4#
Okio(3-Alpha)具有一个ZipFileSystem https://github.com/square/okio/blob/master/okio/src/jvmMain/kotlin/okio/ZipFileSystem.kt
你可以把它和一个读取文件内容的自定义文件系统结合起来,这需要相当多的代码,但是效率很高。
这是自定义文件系统https://github.com/square/okio/blob/88fa50645946bc42725d2f33e143628e7892be1b/okio/src/jvmMain/kotlin/okio/internal/ResourceFileSystem.kt的示例
但我认为将URI转换为文件并避免任何复制或附加代码会更简单。
ux6nzvsh5#
很容易在Android-Kotlin FileAdapter(使用文件管理器)中检查
.zip
和.rar
文件,将以下函数添加到您的代码中: