Skip to content

使用ZipFile进行解压

本文使用Kotlin实现

Android中使用ZipFile与相关类来进行文件解压操作。

  • java.util.zip.ZipEntry 描述zip中的文件
  • java.util.zip.ZipFile 描述zip文件
  • java.util.zip.ZipInputStream 包含ZipEntry信息

解压assets里的zip

本例处理assets中的zip文件。例子中解压得到一份网页文件。

ZipFile需要一个File对象。而assets中的文件不能直接当成File来用。 第一步我们先把目标zip复制一份出来。以下是执行复制操作的代码。

val tempFile = File(targetLocation, "tmp-$assetsZipName.zip")
try {
    val inputStream = assets.open(assetsZipName)
    if (tempFile.exists()) {
        tempFile.delete()
    }
    tempFile.createNewFile()
    val copyOs: OutputStream = FileOutputStream(tempFile)
    val tmp = ByteArray(1024)
    var len: Int
    while (((inputStream.read(tmp)).also { len = it }) != -1) {
        copyOs.write(tmp, 0, len)
    }
    copyOs.flush()
    copyOs.close()
    inputStream.close()
    Log.d(TAG, "临时文件复制完毕")
} catch (e: Exception) {
    Log.e(TAG, "unzipAssetsFile: ", e)
    return
}
如果文件存放在app内部存储或者SD卡,就不用这么麻烦。

得到临时zip后,使用临时zip来进行解压操作。

通过文件得到ZipInputStream,它带有压缩文件内部的各个文件的信息。用ZipEntry来描述。

拿到ZipFile对象,用getInputStream(entry)方法,得到每一个文件(目录)的输入流。然后用流来复制各个压缩的文件。

val zipInputStream = ZipInputStream(FileInputStream(tempFile))
val zipFile = ZipFile(tempFile)
var entry: ZipEntry?

while (zipInputStream.nextEntry.also { entry = it } != null) {
    val outFile = File(targetLocation, entry!!.name)
    Log.d(TAG, "当前文件: $entry -> $outFile")
    if (outFile.parentFile != null && !outFile.parentFile!!.exists()) {
        outFile.parentFile!!.mkdir()
    }

    if (!outFile.exists()) {
        if (entry!!.isDirectory) {
            outFile.mkdirs()
            continue
        } else {
            outFile.createNewFile()
        }
    }

    val bis = BufferedInputStream(zipFile.getInputStream(entry))
    val bos = BufferedOutputStream(FileOutputStream(outFile))
    val entryTmpArr = ByteArray(1024)
    while (true) {
        val readLen = bis.read(entryTmpArr)
        if (readLen == -1) {
            break
        }
        bos.write(entryTmpArr, 0, readLen)
    }
    bos.close()
    bis.close()
    Log.d(TAG, "解压得到文件 $outFile")
}

整个方法代码如下

/**
 * 解压assets里指定的某个zip
 */
private fun unzipAssetsFile(assetsZipName: String, targetLocation: String) {
    Log.d(TAG, "[unzipAssetsFile] targetLocation: $targetLocation")
    val targetDir = File(targetLocation)
    if (!targetDir.exists()) {
        targetDir.mkdirs()
    }
    val tempFile = File(targetLocation, "tmp-$assetsZipName.zip")
    try {
        val inputStream = assets.open(assetsZipName)
        if (tempFile.exists()) {
            tempFile.delete()
        }
        tempFile.createNewFile()
        val copyOs: OutputStream = FileOutputStream(tempFile)
        val tmp = ByteArray(1024)
        var len: Int
        while (((inputStream.read(tmp)).also { len = it }) != -1) {
            copyOs.write(tmp, 0, len)
        }
        copyOs.flush()
        copyOs.close()
        inputStream.close()
        Log.d(TAG, "临时文件复制完毕")
    } catch (e: Exception) {
        Log.e(TAG, "unzipAssetsFile: ", e)
        return
    }

    val zipInputStream = ZipInputStream(FileInputStream(tempFile))
    val zipFile = ZipFile(tempFile)
    var entry: ZipEntry?

    while (zipInputStream.nextEntry.also { entry = it } != null) {
        val outFile = File(targetLocation, entry!!.name)
        Log.d(TAG, "当前文件: $entry -> $outFile")
        if (outFile.parentFile != null && !outFile.parentFile!!.exists()) {
            outFile.parentFile!!.mkdir()
        }

        if (!outFile.exists()) {
            if (entry!!.isDirectory) {
                outFile.mkdirs()
                continue
            } else {
                outFile.createNewFile()
            }
        }

        val bis = BufferedInputStream(zipFile.getInputStream(entry))
        val bos = BufferedOutputStream(FileOutputStream(outFile))
        val entryTmpArr = ByteArray(1024)
        while (true) {
            val readLen = bis.read(entryTmpArr)
            if (readLen == -1) {
                break
            }
            bos.write(entryTmpArr, 0, readLen)
        }
        bos.close()
        bis.close()
        Log.d(TAG, "解压得到文件 $outFile")
    }
    val delTmp = tempFile.delete()
    Log.d(TAG, "解压完毕 删除临时文件$delTmp")
}

电脑压缩zip

mac上如果在Finder里进行压缩,可能在操作的时候,系统创建了一个__MACOS的目录。 为了避免这个目录出现。我们可以用zip命令来压缩。

zip -r target.zip sourceDir

图文无关

作者: RustFisher
联系: rf.cs@foxmail.com
博客: rustfisher.com | RustFisher cnblog
示例: AndroidTutorial Gitee, Tutorial Github
链接: https://www.an.rustfisher.com/android/zip/unzip-file-use-ZipFile/
一家之言,仅当抛砖引玉。如有错漏,还请指出。