You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Worker类完成数据库恢复后无法还原ZIP中的媒体文件

解决方案:先恢复Room数据库再处理媒体文件

核心问题在于你当前的单次ZIP遍历逻辑无法保证先处理数据库再处理所有媒体文件——ZIP条目是按压缩时的顺序存储的,没法在单次遍历中跳过媒体条目先处理数据库,之后再回头处理媒体。下面提供两种可靠的实现思路:

思路1:两次遍历ZIP文件

第一次遍历专门处理数据库文件,第二次遍历处理所有媒体文件。这种方式逻辑简单,适合ZIP文件体积不大的场景:

private fun restoreDataFromZip(context: Context, zipFileUri: Uri): Boolean {
    val contentResolver = context.contentResolver
    var dbRestorationSuccess = false

    // 第一次遍历:仅处理数据库
    contentResolver.openInputStream(zipFileUri)?.use { inputStream ->
        ZipInputStream(inputStream).use { zipInputStream ->
            var entry: ZipEntry?
            while (zipInputStream.nextEntry.also { entry = it } != null) {
                val entryName = entry?.name ?: continue
                if (entryName == "databases/GazeApp.db") {
                    try {
                        restoreDatabaseFile(context, zipInputStream)
                        dbRestorationSuccess = true
                    } catch (e: Exception) {
                        Log.e("DatabaseRestoreWorker", "Failed to restore database", e)
                        return false
                    } finally {
                        zipInputStream.closeEntry()
                    }
                    break // 找到数据库处理完就跳出第一次遍历
                }
                zipInputStream.closeEntry() // 跳过其他条目
            }
        }
    }

    if (!dbRestorationSuccess) return false

    // 第二次遍历:处理所有媒体文件
    contentResolver.openInputStream(zipFileUri)?.use { inputStream ->
        ZipInputStream(inputStream).use { zipInputStream ->
            var entry: ZipEntry?
            while (zipInputStream.nextEntry.also { entry = it } != null) {
                val entryName = entry?.name ?: continue
                if (entryName.startsWith("contacts/")) {
                    try {
                        restoreContactsFile(context, entryName, zipInputStream)
                    } catch (e: Exception) {
                        Log.e("DatabaseRestoreWorker", "Failed to restore media file: $entryName", e)
                        // 可根据需求决定是否终止整个流程
                    } finally {
                        zipInputStream.closeEntry()
                    }
                } else {
                    zipInputStream.closeEntry() // 跳过非媒体条目
                }
            }
        }
    }

    return true
}

思路2:单次遍历缓存媒体条目内容(适合大ZIP文件)

如果ZIP文件体积较大,不想重复打开流,可以在单次遍历中先缓存媒体条目的内容,等数据库处理完成后再处理缓存的媒体:

private fun restoreDataFromZip(context: Context, zipFileUri: Uri): Boolean {
    val contentResolver = context.contentResolver
    var dbRestorationSuccess = false
    val mediaEntries = mutableListOf<Pair<String, ByteArray>>() // 缓存媒体条目名称和内容

    contentResolver.openInputStream(zipFileUri)?.use { inputStream ->
        ZipInputStream(inputStream).use { zipInputStream ->
            var entry: ZipEntry?
            while (zipInputStream.nextEntry.also { entry = it } != null) {
                val entryName = entry?.name ?: continue
                if (entryName == "databases/GazeApp.db") {
                    try {
                        restoreDatabaseFile(context, zipInputStream)
                        dbRestorationSuccess = true
                    } catch (e: Exception) {
                        Log.e("DatabaseRestoreWorker", "Failed to restore database", e)
                        return false
                    } finally {
                        zipInputStream.closeEntry()
                    }
                } else if (entryName.startsWith("contacts/")) {
                    // 缓存媒体文件内容
                    val content = zipInputStream.readBytes()
                    mediaEntries.add(entryName to content)
                    zipInputStream.closeEntry()
                } else {
                    zipInputStream.closeEntry()
                }
            }
        }
    }

    if (!dbRestorationSuccess) return false

    // 处理缓存的媒体文件
    mediaEntries.forEach { (entryName, content) ->
        try {
            val outputFile = File(context.filesDir, entryName)
            outputFile.parentFile?.mkdirs()
            outputFile.writeBytes(content)
        } catch (e: Exception) {
            Log.e("DatabaseRestoreWorker", "Failed to restore media file: $entryName", e)
        }
    }

    return true
}

关键注意事项

  • 数据库连接重置:确保数据库恢复完成后,调用GazeDatabase.resetDatabase()或重新获取数据库实例,避免旧连接导致的读写异常。
  • 异常处理策略:媒体文件恢复失败时,根据业务需求决定是否终止整个流程——如果媒体不是核心数据,可以仅记录日志继续执行。
  • 流的正确关闭:无论是否处理当前条目,都必须调用zipInputStream.closeEntry(),否则会破坏流的状态,导致后续条目读取失败。

内容的提问来源于stack exchange,提问作者Dominik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 07:16:25