This commit is contained in:
kunfei
2023-09-20 16:46:54 +08:00
parent 1f52448138
commit e7af344c55
6 changed files with 54 additions and 389 deletions
-6
View File
@@ -243,12 +243,6 @@ dependencies {
//apache //apache
implementation('org.apache.commons:commons-text:1.10.0') implementation('org.apache.commons:commons-text:1.10.0')
//noinspection GradleDependency,1.23.0对安卓支持不好
implementation 'org.apache.commons:commons-compress:1.22'
implementation 'org.tukaani:xz:1.9'
//RAR
implementation 'com.github.junrar:junrar:7.5.5'
//MarkDown //MarkDown
def markwonVersion = "4.6.2" def markwonVersion = "4.6.2"
@@ -4,8 +4,6 @@ import android.net.Uri
import androidx.annotation.Keep import androidx.annotation.Keep
import cn.hutool.core.codec.Base64 import cn.hutool.core.codec.Base64
import cn.hutool.core.util.HexUtil import cn.hutool.core.util.HexUtil
import com.github.junrar.Archive
import com.github.junrar.rarfile.FileHeader
import com.github.liuyueyi.quick.transfer.ChineseUtils import com.github.liuyueyi.quick.transfer.ChineseUtils
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.dateFormat import io.legado.app.constant.AppConst.dateFormat
@@ -23,13 +21,11 @@ import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.analyzeRule.QueryTTF import io.legado.app.model.analyzeRule.QueryTTF
import io.legado.app.utils.* import io.legado.app.utils.*
import io.legado.app.utils.compress.LibArchiveUtils
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okio.use import okio.use
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry
import org.apache.commons.compress.archivers.sevenz.SevenZFile
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
import org.jsoup.Connection import org.jsoup.Connection
import org.jsoup.Jsoup import org.jsoup.Jsoup
import splitties.init.appCtx import splitties.init.appCtx
@@ -699,18 +695,9 @@ interface JsExtensions : JsEncodeUtils {
HexUtil.decodeHex(url) HexUtil.decodeHex(url)
} }
val bos = ByteArrayOutputStream() return ByteArrayInputStream(bytes).use {
Archive(ByteArrayInputStream(bytes)).use { archive -> LibArchiveUtils.getByteArrayContent(it, path)
var entry: FileHeader
while (archive.nextFileHeader().also { entry = it } != null) {
if (entry.fileName.equals(path)) {
archive.getInputStream(entry).use { it.copyTo(bos) }
return bos.toByteArray()
}
}
} }
log("getRarContent 未发现内容")
return null
} }
/** /**
@@ -726,18 +713,9 @@ interface JsExtensions : JsEncodeUtils {
HexUtil.decodeHex(url) HexUtil.decodeHex(url)
} }
val bos = ByteArrayOutputStream() return ByteArrayInputStream(bytes).use {
SevenZFile(SeekableInMemoryByteChannel(bytes)).use { sevenZFile -> LibArchiveUtils.getByteArrayContent(it, path)
var entry: SevenZArchiveEntry
while (sevenZFile.nextEntry.also { entry = it } != null) {
if (entry.name.equals(path)) {
sevenZFile.getInputStream(entry).use { it.copyTo(bos) }
return bos.toByteArray()
}
}
} }
log("get7zContent 未发现内容")
return null
} }
@@ -10,6 +10,7 @@ import io.legado.app.lib.icu4j.CharsetDetector
import me.zhanghai.android.libarchive.Archive import me.zhanghai.android.libarchive.Archive
import me.zhanghai.android.libarchive.ArchiveEntry import me.zhanghai.android.libarchive.ArchiveEntry
import me.zhanghai.android.libarchive.ArchiveException import me.zhanghai.android.libarchive.ArchiveException
import splitties.init.appCtx
import java.io.File import java.io.File
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.IOException import java.io.IOException
@@ -23,6 +24,7 @@ import java.nio.charset.StandardCharsets
object LibArchiveUtils { object LibArchiveUtils {
val cachePath = File(appCtx.cacheDir, "archive")
@Throws(ArchiveException::class) @Throws(ArchiveException::class)
fun openArchive( fun openArchive(
@@ -60,15 +62,12 @@ object LibArchiveUtils {
Archive.readOpen1(archive) Archive.readOpen1(archive)
successful = true successful = true
return archive return archive
} finally { } finally {
if (!successful) { if (!successful) {
Archive.free(archive) Archive.free(archive)
} }
} }
} }
@@ -135,7 +134,6 @@ object LibArchiveUtils {
} }
} }
} }
@@ -216,7 +214,7 @@ object LibArchiveUtils {
fun unArchive( fun unArchive(
pfd: ParcelFileDescriptor, pfd: ParcelFileDescriptor,
destDir: File, destDir: File,
filter: ((String) -> Boolean)? filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
return unArchive(openArchive(pfd), destDir, filter) return unArchive(openArchive(pfd), destDir, filter)
} }
@@ -288,6 +286,46 @@ object LibArchiveUtils {
return getFilesName(openArchive(pfd), filter) return getFilesName(openArchive(pfd), filter)
} }
fun getByteArrayContent(inputStream: InputStream, path: String): ByteArray? {
val archive = openArchive(inputStream)
try {
var entry = Archive.readNextHeader(archive)
while (entry != 0L) {
val entryName =
getEntryString(ArchiveEntry.pathnameUtf8(entry), ArchiveEntry.pathname(entry))
?: continue
val entryStat = ArchiveEntry.stat(entry)
//判断是否是文件夹
if (S_ISDIR(entryStat.stMode)) {
entry = Archive.readNextHeader(archive)
continue
}
if (entryName == path) {
cachePath.mkdirs()
val entryFile = File(cachePath, entry.toString())
entryFile.delete()
entryFile.createNewFile()
entryFile.setReadable(true)
entryFile.setExecutable(true)
ParcelFileDescriptor.open(entryFile, ParcelFileDescriptor.MODE_WRITE_ONLY).use {
Archive.readDataIntoFd(archive, it.fd)
}
val bytes = entryFile.readBytes()
entryFile.delete()
return bytes
}
entry = Archive.readNextHeader(archive)
}
} finally {
Archive.free(archive)
}
return null
}
@Throws(SecurityException::class) @Throws(SecurityException::class)
private fun getFilesName( private fun getFilesName(
@@ -320,13 +358,11 @@ object LibArchiveUtils {
entry = Archive.readNextHeader(archive) entry = Archive.readNextHeader(archive)
} }
} finally { } finally {
Archive.free(archive) Archive.free(archive)
} }
return fileNames return fileNames
} }
@@ -344,5 +380,4 @@ object LibArchiveUtils {
} }
} }
@@ -1,129 +0,0 @@
package io.legado.app.utils.compress
import com.github.junrar.Archive
import com.github.junrar.rarfile.FileHeader
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
@Suppress("unused", "MemberVisibilityCanBePrivate")
object RarUtils {
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(inputStream: InputStream, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(inputStream, File(path), filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(byteArray: ByteArray, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(byteArray, File(path), filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(zipPath: String, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(zipPath, File(path), filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(file: File, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return unRarToPath(file, File(path), filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(inputStream: InputStream, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(inputStream).use {
unRarToPath(it, destDir, filter)
}
}
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(byteArray: ByteArray, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(ByteArrayInputStream(byteArray)).use {
unRarToPath(it, destDir, filter)
}
}
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(filePath: String, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(File(filePath)).use {
unRarToPath(it, destDir, filter)
}
}
@Throws(NullPointerException::class, SecurityException::class)
fun unRarToPath(file: File, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return Archive(file).use {
unRarToPath(it, destDir, filter)
}
}
@Throws(NullPointerException::class, SecurityException::class)
private fun unRarToPath(
archive: Archive,
destDir: File?,
filter: ((String) -> Boolean)? = null
): List<File> {
destDir ?: throw NullPointerException("解决路径不能为空")
val files = arrayListOf<File>()
var entry: FileHeader?
while (archive.nextFileHeader().also { entry = it } != null) {
val entryName = entry!!.fileName
val entryFile = File(destDir, entryName)
if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) {
throw SecurityException("压缩文件只能解压到指定路径")
}
if (entry!!.isDirectory) {
if (!entryFile.exists()) {
entryFile.mkdirs()
}
continue
}
if (entryFile.parentFile?.exists() != true) {
entryFile.parentFile?.mkdirs()
}
if (filter != null && !filter.invoke(entryName)) continue
if (!entryFile.exists()) {
entryFile.createNewFile()
entryFile.setReadable(true)
entryFile.setExecutable(true)
}
FileOutputStream(entryFile).use {
archive.getInputStream(entry).copyTo(it)
files.add(entryFile)
}
}
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 = mutableListOf<String>()
var entry: FileHeader?
while (archive.nextFileHeader().also { entry = it } != null) {
if (entry!!.isDirectory) {
continue
}
val fileName = entry!!.fileName
if (filter != null && filter.invoke(fileName))
fileNames.add(fileName)
}
return fileNames
}
}
@@ -1,133 +0,0 @@
package io.legado.app.utils.compress
import android.annotation.SuppressLint
import android.os.ParcelFileDescriptor
import io.legado.app.utils.ParcelFileDescriptorChannel
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry
import org.apache.commons.compress.archivers.sevenz.SevenZFile
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.nio.channels.FileChannel
@Suppress("unused", "MemberVisibilityCanBePrivate")
object SevenZipUtils {
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(inputStream: InputStream, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(inputStream, File(path), filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(byteArray: ByteArray, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(byteArray, File(path),filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(pfd: ParcelFileDescriptor, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(pfd, File(path), filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(fileChannel: FileChannel, path: String, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(fileChannel, File(path), filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(inputStream: InputStream, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())), destDir, filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(byteArray: ByteArray, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(SeekableInMemoryByteChannel(byteArray)), destDir, filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(pfd: ParcelFileDescriptor, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(ParcelFileDescriptorChannel(pfd)), destDir, filter)
}
@SuppressLint("NewApi")
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(fileChannel: FileChannel, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(fileChannel), destDir, filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(file: File, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(file), destDir, filter)
}
@Throws(NullPointerException::class, SecurityException::class)
fun un7zToPath(filePath: String, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
return un7zToPath(SevenZFile(File(filePath)), destDir, filter)
}
@Throws(NullPointerException::class, SecurityException::class)
private fun un7zToPath(sevenZFile: SevenZFile, destDir: File?, filter: ((String) -> Boolean)? = null): List<File> {
destDir ?: throw NullPointerException("解决路径不能为空")
val files = arrayListOf<File>()
var entry: SevenZArchiveEntry?
while (sevenZFile.nextEntry.also { entry = it } != null) {
val entryName = entry!!.name
val entryFile = File(destDir, entryName)
if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath)) {
throw SecurityException("压缩文件只能解压到指定路径")
}
if (entry!!.isDirectory) {
if (!entryFile.exists()) {
entryFile.mkdirs()
}
continue
}
if (entryFile.parentFile?.exists() != true) {
entryFile.parentFile?.mkdirs()
}
if (filter != null && !filter.invoke(entryName)) continue
if (!entryFile.exists()) {
entryFile.createNewFile()
entryFile.setReadable(true)
entryFile.setExecutable(true)
}
FileOutputStream(entryFile).use {
sevenZFile.getInputStream(entry).copyTo(it)
files.add(entryFile)
}
}
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 = mutableListOf<String>()
var entry: SevenZArchiveEntry?
while (sevenZFile.nextEntry.also { entry = it } != null) {
if (entry!!.isDirectory) {
continue
}
val fileName = entry!!.name
if (filter != null && filter.invoke(fileName))
fileNames.add(fileName)
}
return fileNames
}
}
@@ -1,14 +1,10 @@
package io.legado.app.utils.compress package io.legado.app.utils.compress
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Build
import androidx.annotation.RequiresApi
import io.legado.app.utils.DebugLog import io.legado.app.utils.DebugLog
import io.legado.app.utils.printOnDebug import io.legado.app.utils.printOnDebug
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.apache.commons.compress.archivers.ArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
import java.io.* import java.io.*
import java.util.zip.* import java.util.zip.*
@@ -215,14 +211,8 @@ object ZipUtils {
path: String, path: String,
filter: ((String) -> Boolean)? = null filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return ZipInputStream(inputStream).use {
ZipArchiveInputStream(inputStream).use { unZipToPath(it, File(path), filter)
unZipToPath(it, File(path), filter)
}
} else {
ZipInputStream(inputStream).use {
unZipToPath(it, File(path), filter)
}
} }
} }
@@ -232,56 +222,11 @@ object ZipUtils {
dir: File, dir: File,
filter: ((String) -> Boolean)? = null filter: ((String) -> Boolean)? = null
): List<File> { ): List<File> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return ZipInputStream(inputStream).use {
ZipArchiveInputStream(inputStream).use { unZipToPath(it, dir, filter)
unZipToPath(it, dir, filter)
}
} else {
ZipInputStream(inputStream).use {
unZipToPath(it, dir, filter)
}
} }
} }
@RequiresApi(Build.VERSION_CODES.N)
@Throws(SecurityException::class)
private fun unZipToPath(
zipInputStream: ZipArchiveInputStream,
dir: File,
filter: ((String) -> Boolean)? = null
): List<File> {
val files = arrayListOf<File>()
var entry: ArchiveEntry?
while (zipInputStream.nextEntry.also { entry = it } != null) {
val entryName = entry!!.name
val entryFile = File(dir, entryName)
if (!entryFile.canonicalPath.startsWith(dir.canonicalPath)) {
throw SecurityException("压缩文件只能解压到指定路径")
}
if (entry!!.isDirectory) {
if (!entryFile.exists()) {
entryFile.mkdirs()
}
continue
}
if (entryFile.parentFile?.exists() != true) {
entryFile.parentFile?.mkdirs()
}
if (filter != null && !filter.invoke(entryName)) continue
if (!entryFile.exists()) {
entryFile.createNewFile()
entryFile.setReadable(true)
entryFile.setExecutable(true)
}
FileOutputStream(entryFile).use {
zipInputStream.copyTo(it)
files.add(entryFile)
}
}
return files
}
@Throws(SecurityException::class) @Throws(SecurityException::class)
private fun unZipToPath( private fun unZipToPath(
zipInputStream: ZipInputStream, zipInputStream: ZipInputStream,
@@ -325,36 +270,11 @@ object ZipUtils {
inputStream: InputStream, inputStream: InputStream,
filter: ((String) -> Boolean)? = null filter: ((String) -> Boolean)? = null
): List<String> { ): List<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return ZipInputStream(inputStream).use {
ZipArchiveInputStream(inputStream).use { getFilesName(it, filter)
getFilesName(it, filter)
}
} else {
ZipInputStream(inputStream).use {
getFilesName(it, filter)
}
} }
} }
@RequiresApi(Build.VERSION_CODES.N)
@Throws(SecurityException::class)
private fun getFilesName(
zipInputStream: ZipArchiveInputStream,
filter: ((String) -> Boolean)? = null
): List<String> {
val fileNames = mutableListOf<String>()
var entry: ArchiveEntry?
while (zipInputStream.nextEntry.also { entry = it } != null) {
if (entry!!.isDirectory) {
continue
}
val fileName = entry!!.name
if (filter != null && filter.invoke(fileName))
fileNames.add(fileName)
}
return fileNames
}
@Throws(SecurityException::class) @Throws(SecurityException::class)
private fun getFilesName( private fun getFilesName(
zipInputStream: ZipInputStream, zipInputStream: ZipInputStream,