当前位置: 首页 > article >正文

Android解压zip rar 7z文件

添加依赖

implementation 'org.apache.commons:commons-compress:1.23.0'
implementation 'com.github.junrar:junrar:7.5.4'//解压rar
implementation 'org.tukaani:xz:1.9'//解压.7z文件需要

下面 FileUtil.kt 代码中用到了 Context 的拓展方法 getAppDir

fun Context.getAppDir() = getExternalFilesDir("")!!.absolutePath

解压文件的代码 FileUtil.kt

import android.content.Context
import android.net.Uri
import android.provider.OpenableColumns
import android.util.Log
import com.github.junrar.Junrar
import org.apache.commons.compress.archivers.ArchiveEntry
import org.apache.commons.compress.archivers.ArchiveStreamFactory
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry
import org.apache.commons.compress.archivers.sevenz.SevenZFile
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipFile
import java.io.Closeable
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.util.Enumeration

private const val ZIP = ".zip"
private const val RAR = ".rar"
private const val SEVEN_Z = ".7z"

object FileUtil {
    private const val TAG = "FileUtil"

    val MIME_TYPES = arrayOf(
        "application/zip", //压缩比例小,耗时少
        "application/rar",
        "application/x-7z-compressed", //压缩比例大,耗时多
    )


    private fun getFileName(context: Context, uri: Uri): String? {
        var filename: String? = null
        val cursor = context.contentResolver.query(uri, null, null, null, null)
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                if (nameIndex > -1) {
                    filename = cursor.getString(nameIndex)
                    Log.i(TAG, "文件名称 $filename")
                }
            }
            cursor.close()
        }
        return filename
    }

    /**
     * 解压文件
     *
     * @return 返回解压后的文件所在的目录
     */
    fun extractFiles(context: Context, uri: Uri): String? {
        val filename = getFileName(context, uri)
        Log.i(TAG, "解压文件 $filename")
        if (filename != null) {
            context.contentResolver.openInputStream(uri)?.use { istream ->
                //由于Android系统对外部存储访问的限制,使用系统框架选取文件,
                //复制到APP专用的外部存储目录中(即 context.getExternalFilesDir("")!!.absolutePath),然后进行解压
                val copyPath = context.getAppDir() + "/" + filename
                FileOutputStream(copyPath).use { ostream ->
                    istream.copyTo(ostream)
                    Log.i(TAG, "文件复制完毕 $copyPath")
                    //解压文件
                    val outDir = when {
                        filename.endsWith(ZIP) -> uncompressFile(context, copyPath, ZipFile::class.java, ZipArchiveEntry::class.java)
                        filename.endsWith(SEVEN_Z) -> uncompressFile(context, copyPath, SevenZFile::class.java, SevenZArchiveEntry::class.java)
                        filename.endsWith(RAR) -> unrar(context, copyPath)
                        else -> null
                    }
                    if (outDir != null) {
                        Log.i(TAG, "解压完毕 $outDir")
                        val deleteSuccess = File(copyPath).delete()
                        Log.i(TAG, "删除临时文件 $copyPath $deleteSuccess")
                    }
                    return outDir
                }
            }
        }
        return null
    }

    private fun extractFiles(context: Context, filePath: String, archiverName: String): String? {
        if (!File(filePath).exists()) {
            return null
        }
        val appDir = context.getAppDir()
        var outDir = appDir
        val factory = ArchiveStreamFactory("UTF-8")
        FileInputStream(filePath).use { istream ->
            // 7z doesn't support streaming
            val archiveInputStream = factory.createArchiveInputStream(archiverName, istream)
            var archiveEntry: ArchiveEntry? = archiveInputStream.nextEntry
            val entryNameList = mutableListOf<String>()
            while (archiveEntry != null) {
                if (archiveEntry.isDirectory) {
                    outDir = appDir + "/" + archiveEntry.name
                    val dir = File(outDir)
                    if (!dir.exists()) dir.mkdirs()
                } else {
                    Log.i(TAG, "压缩包中包含的文件: ${archiveEntry.name}")
                    entryNameList.add(archiveEntry.name)
                }
                archiveEntry = archiveInputStream.nextEntry
            }
            Log.e(TAG, "outDir=$outDir")
            for (entryName in entryNameList) {
                FileOutputStream("$appDir/$entryName").use { ostream ->
                    istream.copyTo(ostream)
                }
            }
            archiveInputStream.close()
        }
        Log.i(TAG, "解压完毕")
        return outDir
    }


    private fun <TFile : Closeable, TArchiveEntry : ArchiveEntry> uncompressFile(
        context: Context, filePath: String,
        cls: Class<TFile>, clsArchiveEntry: Class<TArchiveEntry>
    ): String? {
        val file = File(filePath)
        if (!file.exists()) {
            return null
        }
        val startTime = System.currentTimeMillis()
        val constructor = cls.getDeclaredConstructor(File::class.java)
        val closeableFile = constructor.newInstance(file)
        val entriesObj = cls.getDeclaredMethod("getEntries").invoke(closeableFile)
        val entries = if (entriesObj is Iterable<*>) {
            (entriesObj as Iterable<TArchiveEntry>).toList()
        } else {
            (entriesObj as Enumeration<TArchiveEntry>).toList()
        }
        val appDir = context.getAppDir()
        var outDir = appDir
        val entryList = mutableListOf<TArchiveEntry>()
        for (archiveEntry in entries) {
            if (archiveEntry.isDirectory) {
                outDir = appDir + "/" + archiveEntry.name
                val dir = File(outDir)
                if (!dir.exists()) dir.mkdirs()
            } else {
                Log.i(TAG, "压缩包中的文件: ${archiveEntry.name}")
                entryList.add(archiveEntry)
            }
        }

        val getInputStreamMethod = cls.getDeclaredMethod("getInputStream", clsArchiveEntry)
        for (archiveEntry in entryList) {
            (getInputStreamMethod.invoke(closeableFile, archiveEntry) as InputStream).use { istream ->
                FileOutputStream(appDir + "/" + archiveEntry.name).use { ostream ->
                    istream.copyTo(ostream)
                }
            }
        }
        closeableFile.close()
        val time = System.currentTimeMillis() - startTime
        val suffix = filePath.substring(filePath.lastIndexOf('.'))
        Log.i(TAG, "解压 $suffix 耗时 $time ms")
        return outDir
    }

    /**
     * 解压7z
     */
    private fun un7z(context: Context, filePath: String): String? {
        if (!File(filePath).exists()) {
            return null
        }

        val _7zFile = SevenZFile(File(filePath))
        val files = _7zFile.entries.toList()
        val appDir = context.getAppDir()
        var outDir = appDir
        val entryList = mutableListOf<SevenZArchiveEntry>()
        for (archiveEntry in files) {
            if (archiveEntry.isDirectory) {
                outDir = appDir + "/" + archiveEntry.name
                val dir = File(outDir)
                if (!dir.exists()) dir.mkdirs()
            } else {
                Log.i(TAG, "7Z中包含的文件: ${archiveEntry.name}")
                entryList.add(archiveEntry)
            }
        }
        for (archiveEntry in entryList) {
            _7zFile.getInputStream(archiveEntry).use { istream ->
                FileOutputStream(appDir + "/" + archiveEntry.name).use { ostream ->
                    istream.copyTo(ostream)
                }
            }
        }
        _7zFile.close()
        Log.i(TAG, "7Z解压完毕")
        return outDir
    }


    
    /**
     * 解压rar
     */
    private fun unrar(context: Context, rarFilePath: String): String? {
        val rarFile = File(rarFilePath)
        if (!rarFile.exists()) {
            return null
        }
        val startTime = System.currentTimeMillis()
        val appDir = context.getAppDir()
        var outDir = appDir

        val files = Junrar.extract(rarFile, File(context.getAppDir()))
        for (file in files) {
            if (file.isDirectory) {
                outDir = file.absolutePath
                break
            }
        }
        /*val contentDescriptions: List<ContentDescription> = Junrar.getContentsDescription(rarFile)
        for (item in contentDescriptions) {
            Log.i(TAG, "RAR中文件 ${item.path}")
        }*/
        val time = System.currentTimeMillis() - startTime
        Log.i(TAG, "解压 rar 耗时 $time ms")
        return outDir
    }


    /**
     * 解压zip
     */
    fun unzip(context: Context, zipFilePath: String): String? {
        if (!File(zipFilePath).exists()) {
            return null
        }

        val zipFile = ZipFile(zipFilePath)
        val files = zipFile.entries.toList()
        val appDir = context.getAppDir()
        var outDir = appDir
        val entryList = mutableListOf<ZipArchiveEntry>()
        for (zipEntry in files) {
            if (zipEntry.isDirectory) {
                outDir = appDir + "/" + zipEntry.name
                val dir = File(outDir)
                if (!dir.exists()) dir.mkdirs()
            } else {
                Log.i(TAG, "ZIP中包含的文件: ${zipEntry.name}")
                entryList.add(zipEntry)
            }
        }
        for (zipEntry in entryList) {
            zipFile.getInputStream(zipEntry).use { istream ->
                FileOutputStream(appDir + "/" + zipEntry.name).use { ostream ->
                    istream.copyTo(ostream)
                }
            }
        }
        zipFile.close()
        Log.i(TAG, "ZIP解压完毕")
        return outDir
    }
}

选择文件

val launcher: ActivityResultLauncher<Array<String>> = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
            it?.let { uri ->
                executor.execute {
                    //解压文件
                    FileUtil.extractFiles(context, uri)?.let { outDir ->
                        //获得解压文件所在的目录
                    }
                }
            }
        }

//选择文件
launcher.launch(FileUtil.MIME_TYPES)


http://www.kler.cn/a/37729.html

相关文章:

  • 华为公布多项专利许可计划和费率
  • Spring MVC 注解实现
  • 手机自动化脚本-- 模拟器模拟真机环境过检测
  • 复习第一课 C语言-ubuntu下的命令
  • SpringBoot3中的属性绑定注解和YMAL配置文件、日志
  • 热门好用的免费API接口,包含天气、物流、OCR、短信类等接口
  • Python 列表 insert()函数使用详解
  • 【ArcGIS Pro二次开发】(49):村规数据入库【福建省】
  • springboot聚合项目打包
  • mpVue 微信小程序基于vant-weapp 组件的二次封装TForm 表单组件(适配移动端)——新增仓库地址
  • 基于OpenCV的人脸对齐步骤详解及源码实现
  • 【每日随笔】关于 “ 终身学习 “ ① ( 各阶段学习过程 | 扫盲教育与选拔教育阶段 | 研究生阶段 | 终身学习阶段 )
  • 刘二大人Pytorch课程笔记
  • 【ThinkPHP】ThinkPHP中的门面(Facade)使用
  • 重磅预告丨Fortinet Demo Day系列实战攻防演练来袭!
  • ZooKeeper ZAB
  • VueCli 脚手架使用
  • Spring第二讲:SpringIoC控制反转、依赖注入
  • 考虑设备动作损耗的配电网分布式电压无功优化(Matlab代码实现)
  • 谈谈——互联网生活中的隐私保护