Localbook实现导入压缩文件

This commit is contained in:
Xwite 2023-03-16 12:06:08 +08:00
parent 64b93d0abb
commit b9f86cbb14
8 changed files with 254 additions and 89 deletions

View File

@ -2,8 +2,6 @@ name: Test Build
on: on:
push: push:
branches:
- master
paths: paths:
- '**' - '**'
- '!**/assets/**' - '!**/assets/**'

View File

@ -6,6 +6,7 @@ import androidx.documentfile.provider.DocumentFile
import com.script.SimpleBindings import com.script.SimpleBindings
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppPattern
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.appDb import io.legado.app.data.appDb
@ -31,7 +32,7 @@ import java.util.regex.Pattern
/** /**
* 书籍文件导入 目录正文解析 * 书籍文件导入 目录正文解析
* 支持在线文件(txt epub umd 压缩文件需要用户解压) 本地文件 * 支持在线文件(txt epub umd 压缩文件 本地文件
*/ */
object LocalBook { object LocalBook {
@ -143,7 +144,6 @@ object LocalBook {
/** /**
* 下载在线的文件并自动导入到阅读txt umd epub) * 下载在线的文件并自动导入到阅读txt umd epub)
* 压缩文件请先提示用户解压
*/ */
fun importFileOnLine( fun importFileOnLine(
str: String, str: String,
@ -188,6 +188,23 @@ object LocalBook {
return book return book
} }
fun importArchiveFile(
uri: Uri,
saveFileName: String,
filter: ((String) -> Boolean)? = null
): List<Book> {
val files = ArchiveUtils.deCompress(uri, filter = filter)
if (files.isEmpty()) throw NoStackTraceException(appCtx.getString(R.string.unsupport_archivefile_entry))
return files.map {
saveBookFile(
FileInputStream(it),
saveFileName
).let {
importFile(it)
}
}
}
/** /**
* 从文件分析书籍必要信息书名 作者等 * 从文件分析书籍必要信息书名 作者等
*/ */

View File

@ -534,13 +534,13 @@ class BookInfoActivity :
} else if (webFile.isSupportDecompress) { } else if (webFile.isSupportDecompress) {
/* 解压筛选后再选择导入项 */ /* 解压筛选后再选择导入项 */
viewModel.importOrDownloadWebFile<Uri>(webFile) { uri -> viewModel.importOrDownloadWebFile<Uri>(webFile) { uri ->
viewModel.deCompress(uri) { files -> viewModel.getArchiveEntriesName(uri) { fileNames ->
if (files.size == 1) { if (fileNames.size == 1) {
viewModel.importBook(files[0]) { viewModel.importArchiveBook(uri, fileNames[0]) {
onClick?.invoke(it) onClick?.invoke(it)
} }
} else { } else {
showDecompressFileImportAlert(files, onClick) showDecompressFileImportAlert(uri, fileNames, onClick)
} }
} }
} }
@ -562,19 +562,19 @@ class BookInfoActivity :
} }
private fun showDecompressFileImportAlert( private fun showDecompressFileImportAlert(
files: List<File>, archiveFileUri: Uri,
fileNames: List<String>,
success: ((Book) -> Unit)? = null success: ((Book) -> Unit)? = null
) { ) {
if (files.isEmpty()) { if (fileNames.isEmpty()) {
toastOnUi(R.string.unsupport_archivefile_entry) toastOnUi(R.string.unsupport_archivefile_entry)
return return
} }
val selectorNames = files.map { it.name }
selector( selector(
R.string.import_select_book, R.string.import_select_book,
selectorNames fileNames
) { _, _, index -> ) { _, name, _ ->
viewModel.importBook(files[index]) { viewModel.importArchiveBook(archiveFileUri, name) {
success?.invoke(it) success?.invoke(it)
} }
} }

View File

@ -289,17 +289,17 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
} }
} }
fun deCompress(archiveFileUri: Uri, onSuccess: (List<File>) -> Unit) { fun getArchiveEntriesName(archiveFileUri: Uri, onSuccess: (List<String>) -> Unit) {
execute { execute {
ArchiveUtils.deCompress(archiveFileUri).filter { ArchiveUtils.getArchiveFilesName(archiveFileUri) {
AppPattern.bookFileRegex.matches(it.name) AppPattern.bookFileRegex.matches(it)
} }
}.onError { }.onError {
when (it) { when (it) {
is UnsupportedRarV5Exception -> context.toastOnUi("暂不支持 rar v5 解压") is UnsupportedRarV5Exception -> context.toastOnUi("暂不支持 rar v5 解压")
else -> { else -> {
AppLog.put("DeCompress Error:\n${it.localizedMessage}", it) AppLog.put("getArchiveEntriesName Error:\n${it.localizedMessage}", it)
context.toastOnUi("DeCompress Error:\n${it.localizedMessage}") context.toastOnUi("getArchiveEntriesName Error:\n${it.localizedMessage}")
} }
} }
}.onSuccess { }.onSuccess {
@ -308,19 +308,25 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
} }
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
fun importBook(file: File, success: ((Book) -> Unit)? = null) { fun importArchiveBook(
archiveFileUri: Uri,
archiveEntryName: String,
success: ((Book) -> Unit)? = null
) {
execute { execute {
val suffix = file.name.substringAfterLast(".") val suffix = archiveEntryName.substringAfterLast(".")
LocalBook.saveBookFile( LocalBook.importArchiveFile(
FileInputStream(file), archiveFileUri,
bookData.value!!.getExportFileName(suffix) bookData.value!!.getExportFileName(suffix)
) ) {
it.contains(archiveEntryName)
}.first()
}.onSuccess { }.onSuccess {
val book = changeToLocalBook(LocalBook.importFile(it)) val book = changeToLocalBook(it)
success?.invoke(book) success?.invoke(book)
}.onError { }.onError {
AppLog.put("ImportBook Error:\n${it.localizedMessage}", it) AppLog.put("importArchiveBook Error:\n${it.localizedMessage}", it)
context.toastOnUi("ImportBook Error:\n${it.localizedMessage}") context.toastOnUi("importArchiveBook Error:\n${it.localizedMessage}")
} }
} }

View File

@ -20,50 +20,96 @@ object ArchiveUtils {
fun deCompress( fun deCompress(
archiveUri: Uri, archiveUri: Uri,
path: String = TEMP_PATH path: String = TEMP_PATH,
filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
return deCompress(FileDoc.fromUri(archiveUri, false), path) return deCompress(FileDoc.fromUri(archiveUri, false), path, filter)
} }
fun deCompress( fun deCompress(
archivePath: String, archivePath: String,
path: String = TEMP_PATH path: String = TEMP_PATH,
filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
return deCompress(Uri.parse(archivePath), path) return deCompress(Uri.parse(archivePath), path, filter)
} }
fun deCompress( fun deCompress(
archiveFile: File, archiveFile: File,
path: String = TEMP_PATH path: String = TEMP_PATH,
filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
return deCompress(FileDoc.fromFile(archiveFile), path) return deCompress(FileDoc.fromFile(archiveFile), path, filter)
} }
fun deCompress( fun deCompress(
archiveDoc: DocumentFile, archiveDoc: DocumentFile,
path: String = TEMP_PATH path: String = TEMP_PATH,
filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
return deCompress(FileDoc.fromDocumentFile(archiveDoc), path) return deCompress(FileDoc.fromDocumentFile(archiveDoc), path, filter)
} }
fun deCompress( fun deCompress(
archiveFileDoc: FileDoc, archiveFileDoc: FileDoc,
path: String = TEMP_PATH path: String = TEMP_PATH,
filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
if (archiveFileDoc.isDir) throw IllegalArgumentException("Unexpected Folder input") if (archiveFileDoc.isDir) throw IllegalArgumentException("Unexpected Folder input")
val name = archiveFileDoc.name val name = archiveFileDoc.name
checkAchieve(name)
val workPathFileDoc = getCacheFolderFileDoc(name, path) val workPathFileDoc = getCacheFolderFileDoc(name, path)
val workPath = workPathFileDoc.toString() val workPath = workPathFileDoc.toString()
return archiveFileDoc.openInputStream().getOrThrow().use { return archiveFileDoc.openInputStream().getOrThrow().use {
when { when {
name.endsWith(".zip", ignoreCase = true) -> ZipUtils.unZipToPath(it, workPath) name.endsWith(".zip", ignoreCase = true) -> ZipUtils.unZipToPath(it, workPath, filter)
name.endsWith(".rar", ignoreCase = true) -> RarUtils.unRarToPath(it, workPath) name.endsWith(".rar", ignoreCase = true) -> RarUtils.unRarToPath(it, workPath, filter)
name.endsWith(".7z", ignoreCase = true) -> SevenZipUtils.un7zToPath(it, workPath) name.endsWith(".7z", ignoreCase = true) -> SevenZipUtils.un7zToPath(it, workPath, filter)
else -> throw IllegalArgumentException("Unexpected archive format") else -> throw IllegalArgumentException("Unexpected archive format")
} }
} }
} }
/* 遍历目录获取文件名 */
fun getArchiveFilesName(fileUri: Uri, filter: ((String) -> Boolean)? = null): List<String> = getArchiveEntriesName(FileDoc.fromUri(fileUri, false), filter)
fun getArchiveFilesName(
fileDoc: FileDoc,
filter: ((String) -> Boolean)? = null
): List<String> {
val name = fileDoc.name
checkAchieve(name)
return fileDoc.openInputStream().getOrThrow().use {
when {
name.endsWith(".rar", ignoreCase = true) -> {
RarUtils.getFilesName(it) {
filter?.invoke(it)
}
}
name.endsWith(".zip", ignoreCase = true) -> {
ZipUtils.getFilesName(it) {
filter?.invoke(it)
}
}
name.endsWith(".7z", ignoreCase = true) -> {
SevenZipUtils.getFilesName(it) {
filter?.invoke(it)
}
}
else -> emptyList()
}
}
}
fun checkAchieve(name: String) {
if (
name.endsWith(".zip", ignoreCase = true) ||
name.endsWith(".rar", ignoreCase = true) ||
name.endsWith(".7z", ignoreCase = true)
) return
throw IllegalArgumentException("Unexpected file suffix: Only 7z rar zip Accepted")
}
private fun getCacheFolderFileDoc( private fun getCacheFolderFileDoc(
archiveName: String, archiveName: String,
workPath: String workPath: String

View File

@ -11,60 +11,65 @@ import java.io.InputStream
object RarUtils { object RarUtils {
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(inputStream: InputStream, path: String): List<File> { fun unRarToPath(inputStream: InputStream, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(inputStream, File(path)) return unRarToPath(inputStream, File(path), filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(byteArray: ByteArray, path: String): List<File> { fun unRarToPath(byteArray: ByteArray, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(byteArray, File(path)) return unRarToPath(byteArray, File(path), filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(zipPath: String, path: String): List<File> { fun unRarToPath(zipPath: String, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(zipPath, File(path)) return unRarToPath(zipPath, File(path), filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(file: File, path: String): List<File> { fun unRarToPath(file: File, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(file, File(path)) return unRarToPath(file, File(path), filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(inputStream: InputStream, destDir: File?): List<File> { fun unRarToPath(inputStream: InputStream, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(inputStream).use { return Archive(inputStream).use {
unRarToPath(it, destDir) unRarToPath(it, destDir, filter)
} }
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(byteArray: ByteArray, destDir: File?): List<File> { fun unRarToPath(byteArray: ByteArray, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(ByteArrayInputStream(byteArray)).use { return Archive(ByteArrayInputStream(byteArray)).use {
unRarToPath(it, destDir) unRarToPath(it, destDir, filter)
} }
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(filePath: String, destDir: File?): List<File> { fun unRarToPath(filePath: String, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(File(filePath)).use { return Archive(File(filePath)).use {
unRarToPath(it, destDir) unRarToPath(it, destDir, filter)
} }
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(file: File, destDir: File?): List<File> { fun unRarToPath(file: File, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(file).use { return Archive(file).use {
unRarToPath(it, destDir) unRarToPath(it, destDir, filter)
} }
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
private fun unRarToPath(archive: Archive, destDir: File?): List<File> { private fun unRarToPath(
archive: Archive,
destDir: File?,
filter: ((String) -> Boolean)? = null
): List<File> {
destDir ?: throw NullPointerException("解决路径不能为空") destDir ?: throw NullPointerException("解决路径不能为空")
val files = arrayListOf<File>() val files = arrayListOf<File>()
var entry: FileHeader? var entry: FileHeader?
while (archive.nextFileHeader().also { entry = it } != null) { while (archive.nextFileHeader().also { entry = it } != null) {
val entryFile = File(destDir, entry!!.fileName) val entryName = entry!!.fileName
val entryFile = File(destDir, entryName)
if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) { if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) {
throw SecurityException("压缩文件只能解压到指定路径") throw SecurityException("压缩文件只能解压到指定路径")
} }
@ -77,6 +82,7 @@ object RarUtils {
if (entryFile.parentFile?.exists() != true) { if (entryFile.parentFile?.exists() != true) {
entryFile.parentFile?.mkdirs() entryFile.parentFile?.mkdirs()
} }
if (filter != null && !filter.invoke(entryName)) continue
if (!entryFile.exists()) { if (!entryFile.exists()) {
entryFile.createNewFile() entryFile.createNewFile()
entryFile.setReadable(true) entryFile.setReadable(true)
@ -90,4 +96,32 @@ object RarUtils {
return files return files
} }
/* 遍历目录获取所有文件名 */
@Throws(NullPointerException::class, SecurityException::class)
fun getFilesName(
inputStream: InputStream,
filter: ((String) -> Boolean)? = null
): List<String> {
return Archive(inputStream).use {
getFilesName(it, filter)
}
}
@Throws(NullPointerException::class, SecurityException::class)
private fun getFilesName(
archive: Archive,
filter: ((String) -> Boolean)? = null
): List<String> {
val fileNames = mutableList<String>()
var entry: FileHeader?
while (archive.nextFileHeader().also { entry = it } != null) {
if (entry!!.isDirectory) {
continue
}
fileNames.add(entry!!.fileName)
}
return fileNames
}
} }

View File

@ -15,63 +15,64 @@ import java.nio.channels.FileChannel
object SevenZipUtils { object SevenZipUtils {
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(inputStream: InputStream, path: String): List<File> { fun un7zToPath(inputStream: InputStream, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(inputStream, File(path)) return un7zToPath(inputStream, File(path), filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(byteArray: ByteArray, path: String): List<File> { fun un7zToPath(byteArray: ByteArray, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(byteArray, File(path)) return un7zToPath(byteArray, File(path),filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(pfd: ParcelFileDescriptor, path: String): List<File> { fun un7zToPath(pfd: ParcelFileDescriptor, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(pfd, File(path)) return un7zToPath(pfd, File(path), filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(fileChannel: FileChannel, path: String): List<File> { fun un7zToPath(fileChannel: FileChannel, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(fileChannel, File(path)) return un7zToPath(fileChannel, File(path), filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(inputStream: InputStream, destDir: File?): List<File> { fun un7zToPath(inputStream: InputStream, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())), destDir) return un7zToPath(SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())), destDir, filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(byteArray: ByteArray, destDir: File?): List<File> { fun un7zToPath(byteArray: ByteArray, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(SeekableInMemoryByteChannel(byteArray)), destDir) return un7zToPath(SevenZFile(SeekableInMemoryByteChannel(byteArray)), destDir, filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(pfd: ParcelFileDescriptor, destDir: File?): List<File> { fun un7zToPath(pfd: ParcelFileDescriptor, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(ParcelFileDescriptorChannel(pfd)), destDir) return un7zToPath(SevenZFile(ParcelFileDescriptorChannel(pfd)), destDir, filter)
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(fileChannel: FileChannel, destDir: File?): List<File> { fun un7zToPath(fileChannel: FileChannel, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(fileChannel), destDir) return un7zToPath(SevenZFile(fileChannel), destDir, filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(file: File, destDir: File?): List<File> { fun un7zToPath(file: File, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(file), destDir) return un7zToPath(SevenZFile(file), destDir, filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(filePath: String, destDir: File?): List<File> { fun un7zToPath(filePath: String, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(File(filePath)), destDir) return un7zToPath(SevenZFile(File(filePath)), destDir, filter)
} }
@Throws(NullPointerException::class, SecurityException::class) @Throws(NullPointerException::class, SecurityException::class)
private fun un7zToPath(sevenZFile: SevenZFile, destDir: File?): List<File> { private fun un7zToPath(sevenZFile: SevenZFile, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
destDir ?: throw NullPointerException("解决路径不能为空") destDir ?: throw NullPointerException("解决路径不能为空")
val files = arrayListOf<File>() val files = arrayListOf<File>()
var entry: SevenZArchiveEntry? var entry: SevenZArchiveEntry?
while (sevenZFile.nextEntry.also { entry = it } != null) { while (sevenZFile.nextEntry.also { entry = it } != null) {
val entryFile = File(destDir, entry!!.name) val entryName = entry!!.name
val entryFile = File(destDir, entryName)
if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) { if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) {
throw SecurityException("压缩文件只能解压到指定路径") throw SecurityException("压缩文件只能解压到指定路径")
} }
@ -84,6 +85,7 @@ object SevenZipUtils {
if (entryFile.parentFile?.exists() != true) { if (entryFile.parentFile?.exists() != true) {
entryFile.parentFile?.mkdirs() entryFile.parentFile?.mkdirs()
} }
if (filter != null && !filter.invoke(entryName)) continue
if (!entryFile.exists()) { if (!entryFile.exists()) {
entryFile.createNewFile() entryFile.createNewFile()
entryFile.setReadable(true) entryFile.setReadable(true)
@ -96,4 +98,34 @@ object SevenZipUtils {
} }
return files return files
} }
/* 遍历目录获取所有文件名 */
@Throws(NullPointerException::class, SecurityException::class)
fun getFilesName(
inputStream: InputStream,
filter: ((String) -> Boolean)? = null
): List<String> {
return getFilesName(
SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())),
filter
)
}
@Throws(NullPointerException::class, SecurityException::class)
private fun getFilesName(
sevenZFile: SevenZFile,
filter: ((String) -> Boolean)? = null
): List<String> {
val fileNames = mutableList<String>()
var entry: SevenZArchiveEntry?
while (sevenZFile.nextEntry.also { entry = it } != null) {
if (entry!!.isDirectory) {
continue
}
fileNames.add(entry!!.name)
}
return fileNames
}
} }

View File

@ -179,39 +179,44 @@ object ZipUtils {
} }
@Throws(SecurityException::class) @Throws(SecurityException::class)
fun unZipToPath(file: File, path: String): List<File> { fun unZipToPath(file: File, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return FileInputStream(file).use { return FileInputStream(file).use {
unZipToPath(it, path) unZipToPath(it, path, filter)
} }
} }
@Throws(SecurityException::class) @Throws(SecurityException::class)
fun unZipToPath(file: File, dir: File): List<File> { fun unZipToPath(file: File, dir: File, filter: ((String) -> Boolean)? = null): List<File> {
return FileInputStream(file).use { return FileInputStream(file).use {
unZipToPath(it, dir) unZipToPath(it, dir, filter)
} }
} }
@Throws(SecurityException::class) @Throws(SecurityException::class)
fun unZipToPath(inputStream: InputStream, path: String): List<File> { fun unZipToPath(inputStream: InputStream, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return ZipArchiveInputStream(inputStream).use { return ZipArchiveInputStream(inputStream).use {
unZipToPath(it, File(path)) unZipToPath(it, File(path), filter)
} }
} }
@Throws(SecurityException::class) @Throws(SecurityException::class)
fun unZipToPath(inputStream: InputStream, dir: File): List<File> { fun unZipToPath(inputStream: InputStream, dir: File, filter: ((String) -> Boolean)? = null): List<File> {
return ZipArchiveInputStream(inputStream).use { return ZipArchiveInputStream(inputStream).use {
unZipToPath(it, dir) unZipToPath(it, dir, filter)
} }
} }
@Throws(SecurityException::class) @Throws(SecurityException::class)
private fun unZipToPath(zipInputStream: ZipArchiveInputStream, dir: File): List<File> { private fun unZipToPath(
zipInputStream: ZipArchiveInputStream,
dir: File,
filter: ((String) -> Boolean)? = null
): List<File> {
val files = arrayListOf<File>() val files = arrayListOf<File>()
var entry: ArchiveEntry? var entry: ArchiveEntry?
while (zipInputStream.nextEntry.also { entry = it } != null) { while (zipInputStream.nextEntry.also { entry = it } != null) {
val entryFile = File(dir, entry!!.name) val entryName = entry!!.name
val entryFile = File(dir, entryName)
if (!entryFile.canonicalPath.startsWith(dir.canonicalPath)) { if (!entryFile.canonicalPath.startsWith(dir.canonicalPath)) {
throw SecurityException("压缩文件只能解压到指定路径") throw SecurityException("压缩文件只能解压到指定路径")
} }
@ -224,6 +229,7 @@ object ZipUtils {
if (entryFile.parentFile?.exists() != true) { if (entryFile.parentFile?.exists() != true) {
entryFile.parentFile?.mkdirs() entryFile.parentFile?.mkdirs()
} }
if (filter != null && !filter.invoke(entryName)) continue
if (!entryFile.exists()) { if (!entryFile.exists()) {
entryFile.createNewFile() entryFile.createNewFile()
entryFile.setReadable(true) entryFile.setReadable(true)
@ -237,6 +243,32 @@ object ZipUtils {
return files return files
} }
/* 遍历目录获取所有文件名 */
@Throws(SecurityException::class)
fun getFilesName(
inputStream: InputStream,
filter: ((String) -> Boolean)? = null
): List<String> {
return ZipArchiveInputStream(inputStream).use {
getFilesName(it, filter)
}
}
@Throws(SecurityException::class)
private fun getFilesName(
zipInputStream: ZipArchiveInputStream,
filter: ((String) -> Boolean)? = null
): List<String> {
val fileNames = mutableList<String>()
var entry: ArchiveEntry?
while (zipInputStream.nextEntry.also { entry = it } != null) {
if (entry!!.isDirectory) {
continue
}
fileNames.add(entry!!.name)
}
return fileNames
}
/** /**
* Return the files' path in ZIP file. * Return the files' path in ZIP file.