Android使用Zip4j实现加密压缩

在移动应用开发中,数据安全至关重要。本文将详细介绍如何在Android中使用Zip4j库实现高效安全的加密压缩功能,保护用户敏感数据。

一、为什么选择Zip4j?

Zip4j是一个功能强大的Java库,专为处理加密ZIP文件而设计,相比Android原生Zip工具具有显著优势:

特性 Android原生Zip Zip4j
加密支持 AES和标准ZIP加密
密码保护 不支持 支持
大文件处理 有限 优秀
压缩级别控制 9级可调
文件覆盖处理 基础 高级选项
进度回调 支持

二、环境配置

1. 添加依赖

在模块级build.gradle.kts中添加:

kotlin 复制代码
dependencies {
    implementation("net.lingala.zip4j:zip4j:2.11.5")
    // 最新版本请查看:https://github.com/srikanth-lingala/zip4j
}

2. 权限配置

AndroidManifest.xml中添加存储权限:

xml 复制代码
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="28"/> <!-- Android 9及以下需要 -->

<!-- 对于Android 10+,使用Scoped Storage -->
<application
    android:requestLegacyExternalStorage="true"
    ... >
</application>

三、核心功能实现

1. 基础加密压缩工具类

kotlin 复制代码
import android.content.Context
import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.model.ZipParameters
import net.lingala.zip4j.model.enums.CompressionLevel
import net.lingala.zip4j.model.enums.CompressionMethod
import net.lingala.zip4j.model.enums.EncryptionMethod
import java.io.File

class ZipEncryptor(private val context: Context) {

    /**
     * 加密压缩文件或文件夹
     * @param sourcePath 源路径(文件或文件夹)
     * @param zipFilePath 生成的ZIP文件路径
     * @param password 加密密码
     * @param encryptionMethod 加密方法(默认AES-256)
     */
    fun encryptAndCompress(
        sourcePath: String,
        zipFilePath: String,
        password: String,
        encryptionMethod: EncryptionMethod = EncryptionMethod.AES
    ) {
        val sourceFile = File(sourcePath)
        val zipFile = ZipFile(zipFilePath, password.toCharArray())
        
        val parameters = ZipParameters().apply {
            compressionMethod = CompressionMethod.DEFLATE
            compressionLevel = CompressionLevel.NORMAL
            isEncryptFiles = true
            this.encryptionMethod = encryptionMethod
        }

        when {
            sourceFile.isFile -> zipFile.addFile(sourceFile, parameters)
            sourceFile.isDirectory -> zipFile.addFolder(sourceFile, parameters)
            else -> throw IllegalArgumentException("无效的源路径: $sourcePath")
        }
    }

    /**
     * 安全解压加密ZIP文件
     * @param zipFilePath ZIP文件路径
     * @param destDirectory 解压目标目录
     * @param password 解密密码
     */
    fun decryptAndExtract(
        zipFilePath: String,
        destDirectory: String,
        password: String
    ) {
        val zipFile = ZipFile(zipFilePath, password.toCharArray())
        zipFile.extractAll(destDirectory)
    }
}

2. 带进度回调的高级压缩

kotlin 复制代码
import net.lingala.zip4j.progress.ProgressMonitor

class AdvancedZipEncryptor(context: Context) : ZipEncryptor(context) {

    /**
     * 带进度回调的加密压缩
     * @param progressCallback 进度回调 (当前字节数, 总字节数)
     */
    fun encryptWithProgress(
        sourcePath: String,
        zipFilePath: String,
        password: String,
        progressCallback: (Long, Long) -> Unit
    ) {
        val zipFile = ZipFile(zipFilePath, password.toCharArray())
        val parameters = ZipParameters().apply {
            isEncryptFiles = true
            encryptionMethod = EncryptionMethod.AES
        }
        
        val sourceFile = File(sourcePath)
        
        // 启动进度监控线程
        Thread {
            var lastProgress = 0L
            while (true) {
                val progress = zipFile.progressMonitor
                if (progress.state == ProgressMonitor.State.BUSY) {
                    if (progress.workCompleted > lastProgress) {
                        progressCallback(progress.workCompleted, progress.totalWork)
                        lastProgress = progress.workCompleted
                    }
                } else if (progress.state == ProgressMonitor.State.READY) {
                    break
                }
                Thread.sleep(100)
            }
        }.start()

        // 执行压缩操作
        when {
            sourceFile.isFile -> zipFile.addFile(sourceFile, parameters)
            sourceFile.isDirectory -> zipFile.addFolder(sourceFile, parameters)
        }
    }
}

3. 多文件选择性加密压缩

kotlin 复制代码
fun encryptSelectedFiles(
    filePaths: List<String>,
    zipFilePath: String,
    password: String
) {
    val zipFile = ZipFile(zipFilePath, password.toCharArray())
    val parameters = ZipParameters().apply {
        isEncryptFiles = true
        encryptionMethod = EncryptionMethod.AES
    }
    
    val files = filePaths.map { File(it) }
    zipFile.addFiles(files, parameters)
}

四、Android存储适配

针对不同Android版本处理存储权限:

kotlin 复制代码
import android.os.Build
import android.os.Environment
import androidx.core.content.ContextCompat

fun getSafeStoragePath(context: Context, folderName: String): File {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Android 10+ 使用应用专属目录
        File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), folderName)
    } else {
        // Android 9及以下使用传统存储
        File(Environment.getExternalStorageDirectory(), "Documents/$folderName")
    }.apply { mkdirs() }
}

五、完整使用示例

1. 压缩照片并加密

kotlin 复制代码
fun compressPhotos(context: Context) {
    val zipUtil = ZipEncryptor(context)
    val photosDir = getSafeStoragePath(context, "Photos")
    val zipPath = File(getSafeStoragePath(context, "Archives"), "photos_encrypted.zip").absolutePath
    
    try {
        zipUtil.encryptAndCompress(
            sourcePath = photosDir.absolutePath,
            zipFilePath = zipPath,
            password = "Secure@Password123!",
            encryptionMethod = EncryptionMethod.AES
        )
        Toast.makeText(context, "照片加密压缩完成", Toast.LENGTH_SHORT).show()
    } catch (e: Exception) {
        Log.e("ZipError", "压缩失败: ${e.message}")
        Toast.makeText(context, "压缩失败: ${e.message}", Toast.LENGTH_LONG).show()
    }
}

2. 带进度条的大文件压缩

kotlin 复制代码
fun compressLargeFileWithProgress(context: Context, progressBar: ProgressBar) {
    val advancedZip = AdvancedZipEncryptor(context)
    val largeFilePath = getSafeStoragePath(context, "Videos").resolve("large_video.mp4").absolutePath
    val zipPath = File(getSafeStoragePath(context, "Archives"), "video_encrypted.zip").absolutePath
    
    advancedZip.encryptWithProgress(
        sourcePath = largeFilePath,
        zipFilePath = zipPath,
        password = "StrongPassword!456"
    ) { current, total ->
        // 更新UI进度
        runOnUiThread {
            progressBar.max = total.toInt()
            progressBar.progress = current.toInt()
        }
    }
}

3. 解压加密文件

kotlin 复制代码
fun extractEncryptedZip(context: Context) {
    val zipUtil = ZipEncryptor(context)
    val zipPath = File(getSafeStoragePath(context, "Archives"), "photos_encrypted.zip").absolutePath
    val extractDir = getSafeStoragePath(context, "ExtractedPhotos")
    
    try {
        zipUtil.decryptAndExtract(
            zipFilePath = zipPath,
            destDirectory = extractDir.absolutePath,
            password = "Secure@Password123!"
        )
        Toast.makeText(context, "文件解压成功", Toast.LENGTH_SHORT).show()
    } catch (e: Exception) {
        Log.e("ZipError", "解压失败: ${e.message}")
        Toast.makeText(context, "解压失败: 密码错误或文件损坏", Toast.LENGTH_LONG).show()
    }
}

六、高级配置选项

1. 自定义压缩参数

kotlin 复制代码
fun getCustomZipParameters(): ZipParameters {
    return ZipParameters().apply {
        compressionMethod = CompressionMethod.DEFLATE
        compressionLevel = CompressionLevel.MAXIMUM // 最高压缩率
        isEncryptFiles = true
        encryptionMethod = EncryptionMethod.AES
        aesKeyStrength = AesKeyStrength.KEY_STRENGTH_256 // 256位AES加密
        
        // 文件覆盖选项
        fileExistsInZipMode = FileExistsInZipMode.OVERWRITE
        
        // 设置文件注释
        fileComment = "Created on ${SimpleDateFormat("yyyy-MM-dd").format(Date())}"
        
        // 设置解压后文件属性(Unix系统)
        unixFileAttributes = 644
    }
}

2. 分卷压缩(适合大文件)

kotlin 复制代码
fun splitZipCreation(context: Context) {
    val sourceFile = getSafeStoragePath(context, "Database").resolve("backup.db")
    val zipPath = File(getSafeStoragePath(context, "Archives"), "db_backup.zip").absolutePath
    
    val zipFile = ZipFile(zipPath, "backupPassword123!".toCharArray())
    val parameters = ZipParameters().apply {
        isEncryptFiles = true
        encryptionMethod = EncryptionMethod.AES
    }
    
    // 设置分卷大小(100MB)
    zipFile.isSplitArchive = true
    zipFile.splitLength = 100 * 1024 * 1024 // 100MB
    
    zipFile.addFile(sourceFile, parameters)
}

七、常见问题解决方案

1. 密码强度问题

错误信息net.lingala.zip4j.exception.ZipException: Password is too weak
解决方案

kotlin 复制代码
fun isStrongPassword(password: String): Boolean {
    // 至少8位,包含大小写字母、数字和特殊字符
    val pattern = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$".toRegex()
    return pattern.matches(password)
}

2. 存储权限问题

错误信息java.io.FileNotFoundException: ... (Permission denied)
解决方案

kotlin 复制代码
// 在Activity中请求权限
private fun requestStoragePermission() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
            STORAGE_PERMISSION_CODE
        )
    } else {
        // Android 10+ 不需要请求存储权限
        startCompression()
    }
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    if (requestCode == STORAGE_PERMISSION_CODE && 
        grantResults.isNotEmpty() && 
        grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        startCompression()
    } else {
        Toast.makeText(this, "需要存储权限才能执行此操作", Toast.LENGTH_LONG).show()
    }
}

3. 内存优化

处理大文件时使用流式压缩避免OOM:

kotlin 复制代码
fun streamCompression(sourceFile: File, zipPath: String, password: String) {
    val zipFile = ZipFile(zipPath, password.toCharArray())
    val parameters = ZipParameters().apply {
        isEncryptFiles = true
        encryptionMethod = EncryptionMethod.AES
    }
    
    FileInputStream(sourceFile).use { fis ->
        zipFile.addStream(fis, parameters)
    }
}

八、性能优化建议

  1. 压缩级别选择

    • CompressionLevel.FASTEST:速度最快,压缩率低
    • CompressionLevel.NORMAL:平衡模式(默认)
    • CompressionLevel.MAXIMUM:最高压缩率,速度慢
  2. 多线程压缩(Zip4j 2.x+):

kotlin 复制代码
zipFile.setRunInThread(true) // 启用后台线程压缩
  1. 文件筛选(避免压缩不必要文件):
kotlin 复制代码
parameters.fileExclusionFilters = listOf(
    FileFilter { file -> file.name.endsWith(".tmp") },
    FileFilter { file -> file.name.startsWith("temp_") }
)

九、总结与最佳实践

关键点总结

  1. 安全优先:始终使用AES-256加密保护敏感数据
  2. 密码策略:强制用户使用强密码(8+字符,混合类型)
  3. 存储适配:正确处理Android不同版本的存储权限
  4. 进度反馈:对大文件操作提供进度显示
  5. 错误处理:优雅处理密码错误、文件损坏等情况
  6. 资源管理:及时关闭文件流,避免资源泄漏

推荐实践

kotlin 复制代码
fun safeZipOperation(context: Context) {
    try {
        // 1. 验证源文件存在
        val sourceFile = File(sourcePath)
        if (!sourceFile.exists()) throw FileNotFoundException("源文件不存在")
        
        // 2. 验证目标路径可写
        val zipFile = File(zipPath)
        if (zipFile.exists() && !zipFile.canWrite()) throw IOException("目标文件不可写")
        
        // 3. 验证密码强度
        if (!isStrongPassword(password)) throw IllegalArgumentException("密码强度不足")
        
        // 4. 执行压缩
        val zipUtil = ZipEncryptor(context)
        zipUtil.encryptAndCompress(sourcePath, zipPath, password)
        
        // 5. 验证结果
        if (!zipFile.exists() || zipFile.length() == 0L) {
            throw IOException("压缩文件创建失败")
        }
        
        // 6. 清理临时文件(如果需要)
        // ...
        
    } catch (e: Exception) {
        Log.e("ZipOperation", "操作失败", e)
        // 通知用户并提供解决方案
    }
}

Zip4j为Android提供了强大的加密压缩能力,合理使用可以显著提升应用的数据安全性。建议在实际项目中根据具体需求选择合适的压缩参数和加密级别,并在关键操作中添加适当的日志记录和异常处理。

相关推荐
移动开发者1号10 分钟前
解析 Android Doze 模式与唤醒对齐
android·kotlin
Devil枫2 小时前
Kotlin扩展函数与属性
开发语言·python·kotlin
菠萝加点糖2 小时前
Kotlin Data包含ByteArray类型
android·开发语言·kotlin
IAM四十二9 天前
Google 端侧 AI 框架 LiteRT 初探
android·深度学习·tensorflow
CYRUS_STUDIO9 天前
手把手教你用 Chrome 断点调试 Frida 脚本,JS 调试不再是黑盒
android·app·逆向
Just丶Single10 天前
安卓NDK初识
android
编程乐学10 天前
网络资源模板--基于Android Studio 实现的咖啡点餐App
android·android studio·大作业·奶茶点餐·安卓移动开发·咖啡点餐
二流小码农10 天前
鸿蒙开发:基于node脚本实现组件化运行
android·ios·harmonyos
Wgllss10 天前
Kotlin+协程+FLow+Channel+Compose 实现一个直播多个弹幕效果
android·架构·android jetpack
续天续地10 天前
开箱即用的Kotlin Multiplatform 跨平台开发模板:覆盖网络/存储/UI/DI/CI工具链
ios·kotlin