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()
            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  uncompressFile(
        context: Context, filePath: String,
        cls: Class, clsArchiveEntry: Class
    ): 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).toList()
        } else {
            (entriesObj as Enumeration).toList()
        }
        val appDir = context.getAppDir()
        var outDir = appDir
        val entryList = mutableListOf()
        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()
        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 = 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()
        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> = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
            it?.let { uri ->
                executor.execute {
                    //解压文件
                    FileUtil.extractFiles(context, uri)?.let { outDir ->
                        //获得解压文件所在的目录
                    }
                }
            }
        }

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

你可能感兴趣的:(Android开发,android)