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

相关推荐
幻雨様5 小时前
UE5多人MOBA+GAS 45、制作冲刺技能
android·ue5
Jerry说前后端6 小时前
Android 数据可视化开发:从技术选型到性能优化
android·信息可视化·性能优化
Meteors.7 小时前
Android约束布局(ConstraintLayout)常用属性
android
alexhilton8 小时前
玩转Shader之学会如何变形画布
android·kotlin·android jetpack
whysqwhw12 小时前
安卓图片性能优化技巧
android
风往哪边走12 小时前
自定义底部筛选弹框
android
Yyyy48213 小时前
MyCAT基础概念
android
Android轮子哥13 小时前
尝试解决 Android 适配最后一公里
android
雨白14 小时前
OkHttp 源码解析:enqueue 非同步流程与 Dispatcher 调度
android
风往哪边走15 小时前
自定义仿日历组件弹框
android