diff --git a/app/build.gradle b/app/build.gradle index eca03b2e2..b0b8c0e53 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -232,6 +232,7 @@ dependencies { //apache implementation('org.apache.commons:commons-text:1.10.0') implementation 'org.apache.commons:commons-compress:1.22' + implementation 'org.tukaani:xz:1.9' //RAR implementation 'com.github.junrar:junrar:7.5.4' diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 3ae189731..51028028c 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -4,6 +4,8 @@ import android.net.Uri import androidx.annotation.Keep import cn.hutool.core.codec.Base64 import cn.hutool.core.util.HexUtil +import com.github.junrar.Archive +import com.github.junrar.rarfile.FileHeader import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst.dateFormat import io.legado.app.constant.AppLog @@ -21,6 +23,10 @@ import io.legado.app.utils.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking +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.Jsoup import splitties.init.appCtx @@ -312,21 +318,21 @@ interface JsExtensions : JsEncodeUtils { } return response } - + /* Str转ByteArray */ fun strToBytes(str: String): ByteArray { return str.toByteArray(charset("UTF-8")) } - + fun strToBytes(str: String, charset: String): ByteArray { return str.toByteArray(charset(charset)) } - + /* ByteArray转Str */ fun bytesToStr(bytes: ByteArray): String { return String(bytes, charset("UTF-8")) } - + fun bytesToStr(bytes: ByteArray, charset: String): String { return String(bytes, charset(charset)) } @@ -337,7 +343,7 @@ interface JsExtensions : JsEncodeUtils { fun base64Decode(str: String?): String { return Base64.decodeStr(str) } - + fun base64Decode(str: String?, charset: String): String { return Base64.decodeStr(str, charset(charset)) } @@ -481,7 +487,7 @@ interface JsExtensions : JsEncodeUtils { } /** - * js实现压缩文件解压 + * js实现Zip压缩文件解压 * @param zipPath 相对路径 * @return 相对路径 */ @@ -497,6 +503,71 @@ interface JsExtensions : JsEncodeUtils { FileUtils.delete(zipFile.absolutePath) return unzipPath.substring(FileUtils.getCachePath().length) } + /** + * js实现7Zip压缩文件解压 + * @param zipPath 相对路径 + * @return 相对路径 + */ + fun un7zFile(zipPath: String): String { + if (zipPath.isEmpty()) return "" + val unzipPath = FileUtils.getPath( + FileUtils.createFolderIfNotExist(FileUtils.getCachePath()), + FileUtils.getNameExcludeExtension(zipPath) + ) + val unzipFolder = File(unzipPath).createFolderReplace() + val zipFile = getFile(zipPath) + SevenZipUtils.un7zToPath(zipFile, unzipFolder) + FileUtils.delete(zipFile.absolutePath) + return unzipPath.substring(FileUtils.getCachePath().length) + } + /** + * js实现Rar压缩文件解压 + * @param zipPath 相对路径 + * @return 相对路径 + */ + fun unrarFile(zipPath: String): String { + if (zipPath.isEmpty()) return "" + val unzipPath = FileUtils.getPath( + FileUtils.createFolderIfNotExist(FileUtils.getCachePath()), + FileUtils.getNameExcludeExtension(zipPath) + ) + val unzipFolder = File(unzipPath).createFolderReplace() + val zipFile = getFile(zipPath) + RarUtils.unRarToPath(zipFile, unzipFolder) + FileUtils.delete(zipFile.absolutePath) + return unzipPath.substring(FileUtils.getCachePath().length) + } + /** + * js实现压缩文件解压 + * @param zipPath 相对路径 + * @return 相对路径 + */ + fun unArchiveFile(zipPath: String): String { + if (zipPath.isEmpty()) return "" + val unzipPath = FileUtils.getPath( + FileUtils.createFolderIfNotExist(FileUtils.getCachePath()), + FileUtils.getNameExcludeExtension(zipPath) + ) + val unzipFolder = File(unzipPath).createFolderReplace() + val zipFile = getFile(zipPath) + when { + zipPath.endsWith(".zip", ignoreCase = true) -> { + ZipUtils.unzipFile(zipFile, unzipFolder) + } + zipPath.endsWith(".rar", ignoreCase = true) -> { + RarUtils.unRarToPath(zipFile, unzipFolder) + } + + zipPath.endsWith(".7z", ignoreCase = true) -> { + SevenZipUtils.un7zToPath(zipFile, unzipFolder) + } + else -> { + log("自动解压未识别类型${zipPath}") + } + } + FileUtils.delete(zipFile.absolutePath) + return unzipPath.substring(FileUtils.getCachePath().length) + } /** * js实现文件夹内所有文本文件读取 @@ -538,6 +609,40 @@ interface JsExtensions : JsEncodeUtils { return String(byteArray, Charset.forName(charsetName)) } + /** + * 获取网络zip文件里面的数据 + * @param url zip文件的链接或十六进制字符串 + * @param path 所需获取文件在zip内的路径 + * @return zip指定文件的数据 + */ + fun getRarStringContent(url: String, path: String): String { + val byteArray = getRarByteArrayContent(url, path) ?: return "" + val charsetName = EncodingDetect.getEncode(byteArray) + return String(byteArray, Charset.forName(charsetName)) + } + + fun getRarStringContent(url: String, path: String, charsetName: String): String { + val byteArray = getRarByteArrayContent(url, path) ?: return "" + return String(byteArray, Charset.forName(charsetName)) + } + + /** + * 获取网络7zip文件里面的数据 + * @param url 7zip文件的链接或十六进制字符串 + * @param path 所需获取文件在7zip内的路径 + * @return zip指定文件的数据 + */ + fun get7zStringContent(url: String, path: String): String { + val byteArray = get7zByteArrayContent(url, path) ?: return "" + val charsetName = EncodingDetect.getEncode(byteArray) + return String(byteArray, Charset.forName(charsetName)) + } + + fun get7zStringContent(url: String, path: String, charsetName: String): String { + val byteArray = get7zByteArrayContent(url, path) ?: return "" + return String(byteArray, Charset.forName(charsetName)) + } + /** * 获取网络zip文件里面的数据 * @param url zip文件的链接或十六进制字符串 @@ -551,19 +656,75 @@ interface JsExtensions : JsEncodeUtils { HexUtil.decodeHex(url) } val bos = ByteArrayOutputStream() - val zis = ZipInputStream(ByteArrayInputStream(bytes)) - var entry: ZipEntry? = zis.nextEntry - while (entry != null) { - if (entry.name.equals(path)) { - zis.use { it.copyTo(bos) } - return bos.toByteArray() + ZipInputStream(ByteArrayInputStream(bytes)).use { zis -> + var entry: ZipEntry + while (zis.nextEntry.also { entry = it } != null) { + if (entry.name.equals(path)) { + zis.use { it.copyTo(bos) } + return bos.toByteArray() + } + entry = zis.nextEntry } - entry = zis.nextEntry } + log("getZipContent 未发现内容") return null } + /** + * 获取网络Rar文件里面的数据 + * @param url Rar文件的链接或十六进制字符串 + * @param path 所需获取文件在Rar内的路径 + * @return Rar指定文件的数据 + */ + fun getRarByteArrayContent(url: String, path: String): ByteArray? { + val bytes = if (url.isAbsUrl()) { + AnalyzeUrl(url, source = getSource()).getByteArray() + } else { + HexUtil.decodeHex(url) + } + + val bos = ByteArrayOutputStream() + Archive(ByteArrayInputStream(bytes)).use { archive -> + 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 + } + + /** + * 获取网络7zip文件里面的数据 + * @param url 7zip文件的链接或十六进制字符串 + * @param path 所需获取文件在7zip内的路径 + * @return 7zip指定文件的数据 + */ + fun get7zByteArrayContent(url: String, path: String): ByteArray? { + val bytes = if (url.isAbsUrl()) { + AnalyzeUrl(url, source = getSource()).getByteArray() + } else { + HexUtil.decodeHex(url) + } + + val bos = ByteArrayOutputStream() + SevenZFile(SeekableInMemoryByteChannel(bytes)).use { sevenZFile -> + 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 + } + //******************文件操作************************// diff --git a/app/src/main/java/io/legado/app/utils/RarUtils.kt b/app/src/main/java/io/legado/app/utils/RarUtils.kt index ac3cc8582..3303d7c41 100644 --- a/app/src/main/java/io/legado/app/utils/RarUtils.kt +++ b/app/src/main/java/io/legado/app/utils/RarUtils.kt @@ -1,17 +1,57 @@ package io.legado.app.utils +import androidx.annotation.Keep 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") + +@Keep +@Suppress("unused","MemberVisibilityCanBePrivate") object RarUtils { - fun unRarToPath(inputStream: InputStream, path: String){ - val archive= Archive(inputStream) + fun unRarToPath(inputStream: InputStream,path:String){ + unRarToPath(inputStream, File(path)) + } + fun unRarToPath(byteArray: ByteArray,path:String){ + unRarToPath(byteArray, File(path)) + } + fun unRarToPath(zipPath:String,path:String){ + unRarToPath(zipPath, File(path)) + } + fun unRarToPath(file: File,path:String){ + unRarToPath(file, File(path)) + } + fun unRarToPath(inputStream: InputStream, destDir: File?) { + Archive(inputStream).use { + unRarToPath(it, destDir) + } + inputStream.close() + } + + fun unRarToPath(byteArray: ByteArray, destDir: File?) { + Archive(ByteArrayInputStream(byteArray)).use { + unRarToPath(it, destDir) + } + } + + fun unRarToPath(filePath:String, destDir: File?) { + Archive(File(filePath)).use { + unRarToPath(it, destDir) + } + } + + fun unRarToPath(file: File, destDir: File?) { + Archive(file).use { + unRarToPath(it, destDir) + } + } + + fun unRarToPath(archive: Archive, destDir: File?) { var entry: FileHeader - while (archive.nextFileHeader().also { entry=it }!=null){ - val entryFile = File(path, entry.fileName) + while (archive.nextFileHeader().also { entry = it } != null) { + val entryFile = File(destDir, entry.fileName) if (entry.isDirectory) { if (!entryFile.exists()) { entryFile.mkdirs() @@ -31,7 +71,6 @@ object RarUtils { } } - inputStream.close() } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/utils/SevenZipUtils.kt b/app/src/main/java/io/legado/app/utils/SevenZipUtils.kt index 6c5be50ac..b733ab463 100644 --- a/app/src/main/java/io/legado/app/utils/SevenZipUtils.kt +++ b/app/src/main/java/io/legado/app/utils/SevenZipUtils.kt @@ -2,6 +2,7 @@ package io.legado.app.utils import android.annotation.SuppressLint import android.os.ParcelFileDescriptor +import androidx.annotation.Keep import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry import org.apache.commons.compress.archivers.sevenz.SevenZFile import org.apache.commons.compress.utils.SeekableInMemoryByteChannel @@ -9,30 +10,49 @@ import java.io.File import java.io.FileOutputStream import java.io.InputStream import java.nio.channels.FileChannel -@Suppress("unused") + +@Keep +@Suppress("unused","MemberVisibilityCanBePrivate") object SevenZipUtils { - - - fun un7zToPath(inputStream: InputStream, path: String){ - un7zToPath(SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())),path) + fun un7zToPath(inputStream: InputStream, path:String){ + un7zToPath(inputStream,File(path)) } - fun un7zToPath(pfd: ParcelFileDescriptor, path: String){ - un7zToPath(SevenZFile(ParcelFileDescriptorChannel(pfd)),path) + fun un7zToPath(byteArray: ByteArray, path:String){ + un7zToPath(byteArray,File(path)) + } + fun un7zToPath(pfd: ParcelFileDescriptor, path:String){ + un7zToPath(pfd,File(path)) + } + fun un7zToPath(fileChannel: FileChannel, path:String){ + un7zToPath(fileChannel,File(path)) + } + + fun un7zToPath(inputStream: InputStream, destDir: File?){ + un7zToPath(SevenZFile(SeekableInMemoryByteChannel(inputStream.readBytes())),destDir) + } + fun un7zToPath(byteArray: ByteArray, destDir: File?){ + un7zToPath(SevenZFile(SeekableInMemoryByteChannel(byteArray)),destDir) + } + fun un7zToPath(pfd: ParcelFileDescriptor, destDir: File?){ + un7zToPath(SevenZFile(ParcelFileDescriptorChannel(pfd)),destDir) } @SuppressLint("NewApi") - fun un7zToPath(fileChannel: FileChannel, path: String){ - un7zToPath(SevenZFile(fileChannel),path) + fun un7zToPath(fileChannel: FileChannel, destDir: File?){ + un7zToPath(SevenZFile(fileChannel),destDir) } - fun un7zToPath(file: File, path: String){ - un7zToPath(SevenZFile(file),path) + fun un7zToPath(file: File, destDir: File?){ + un7zToPath(SevenZFile(file),destDir) + } + fun un7zToPath(filePath: String, destDir: File?){ + un7zToPath(SevenZFile(File(filePath)),destDir) } - fun un7zToPath(sevenZFile:SevenZFile, path: String){ + fun un7zToPath(sevenZFile:SevenZFile, destDir: File?){ var entry: SevenZArchiveEntry while (sevenZFile.nextEntry.also { entry=it }!=null){ - val entryFile = File(path, entry.name) + val entryFile = File(destDir, entry.name) if (entry.isDirectory) { if (!entryFile.exists()) { entryFile.mkdirs()