android - Android:检查选定的数据库 .db 文件是否有效
问题描述
在我的应用程序中,我可以用用户选择的另一个文件替换当前数据库。
我只是想要一种方法来检查用户选择的文件是否是有效的数据库文件。
我让用户选择这样的文件:
private fun selectDbFile() {
val intent: Intent
val selectIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
intent = Intent.createChooser(selectIntent, "Choose a file")
intentLauncher.launch(intent)
}
并且 intentLauncher 定义如下:
private var intentLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
val uri = data?.data
uri?.let{
UtilityKt.replaceCurrentDb(requireContext(), uri) {
showSuccessAlert()
}
}
}
}
用新的替换旧的db文件的方法是这样的:
fun replaceCurrentDb(context: Context, newDbUri: Uri?, completion: () -> Unit) {
newDbUri?.let { newUri ->
GlobalScope.launch(Dispatchers.IO) {
// Do background work
try {
val currentDb = context.getDatabasePath(MenuSQLiteHelper.DATABASE_NAME)
if (currentDb.exists()) {
val src = context.contentResolver.openInputStream(newUri)
val dst = FileOutputStream(currentDb)
val buffer = ByteArray(8 * 1024)
src?.copyTo(dst, buffer.size)
src?.close()
dst.close()
// Back to main thread
launch(Dispatchers.Main) {
completion()
}
}
}
catch (e: IOException) {
Toast.makeText(context, "Operation failed.", Toast.LENGTH_LONG).show()
Log.e("DOWNLOAD FAIL!!!", " EXCEPTION IS:", e)
}
}
}
}
只有当用户选择一个有效的 .db 文件时它才能正常工作,但是如果他选择了一个图像,例如,应用程序会崩溃,因为错误的数据被写入了目的地。
解决方案
我只是想要一种方法来检查用户选择的文件是否是有效的数据库文件。
如果您的意思是任何 SQLite 数据库,那么您可以检查标题,前 16 个字节将是"SQLite format 3\000"。这是检查此功能的示例:-
private fun isFileSQLiteDatabase(f: File): Boolean {
val SQLITEFILEHEADER = "SQLite format 3\u0000"
val fis: InputStream
if (!f.isFile) return false
val header = ByteArray(16)
try {
fis = FileInputStream(f)
fis.read(header)
if (String(header) != SQLITEFILEHEADER) {
fis.close()
return false
} else {
fis.close()
}
} catch (e: IOException) {
return false
}
return true
}
但是,如果您想确保它不是任何SQLite 数据库,例如遵循一组模式的数据库,那么您需要进行更深入的检查,例如检查表和/或其他实体(触发器,索引,视图)与预期的一样。
例如,以下函数通过询问模式(即 sqlite_master)传递了预期实体名称的数组:-
private fun isDBValid(f: File, entityList: Array<String>): Boolean {
var matchcount = 0
if (!isFileSQLiteDatabase(f)) return false
try {
val db = SQLiteDatabase.openDatabase(f.path, null, SQLiteDatabase.OPEN_READWRITE)
val csr = db.query("sqlite_master", null, null, null, null, null, null)
while (csr.moveToNext()) {
for (s in entityList) {
if (s.toLowerCase() == csr.getString(csr.getColumnIndex("name")).toLowerCase()) {
matchcount++
break
}
}
}
csr.close()
db.close()
} catch (e: SQLiteException) {
return false;
}
return matchcount != entityList.size
}
- 请注意,这会调用上一个函数。
推荐阅读
- symlink - msys2 中的符号链接:复制还是硬链接?
- java - 备份不包括表的 derby 数据库
- oracle - Oracle - 舍入所有列中的所有数值
- python - “语法错误:非默认参数遵循默认参数”的含义
- android - 使用 SurfaceView 在活动上显示插页式广告的行为
- typescript - 打字稿接口:除了已知密钥之外的其他任何东西都必须是
? - javascript - 何检查内部文本是否小于 5 并在其中应用一个类
- java - 如何使用 jwt 为用户名和密码配置 webSecurity?
- php - 来自 mysqli 的 HTML 选择返回不完整的行数据
- office-js - 如何在 Officejs 中使用任务窗格实现启用/禁用命令