将媒体文件路径或 URI 保存到 SQLite 并获取它,最佳实践
Saving media file path or URI to SQLite and getting it, best practice
我的目标是:
- 将媒体文件保存到外部存储(在我的例子中是照片)。
- 获取保存数据的文件路径或 URI。
- 将其保存到 SQLite(文件路径或内容 URI 或其他)。
- 能够在未来的任何时候获得此内容的正确 URI。
这与其他非常流行的应用程序所做的非常相似 - 他们在 'Pictures' 文件夹中创建目录并将照片存储在那里并在他们的应用程序中使用它们,同时也可以使用 [=45= 查看它们] 探险家等
据我所知,保存媒体内容(图片,f.e.)的推荐方法是使用 MediaStore
API 结果我得到了内容 URI,我可以使用它之后。
但后来我读到这些内容 URI 可能会在重新扫描媒体后发生更改,因此看起来不可靠。 (比如用了SD卡又拔出来再插)
同时不推荐使用绝对文件路径,并且有弃用使用绝对文件路径与外部存储一起使用的 APIs 的倾向。所以看起来也不靠谱
我只能想象以下解决方案:
- 保存时使用唯一的自动生成的文件名(如 UUID)。
- 当我需要获取内容 URI 时(f.e。想在
ImageView
中渲染照片)- 我可以使用 ContentResolver
并使用文件名过滤器搜索内容 URI。
这种方法的问题是我有很多照片(图库),使用 ContentResolver
查询会显着影响性能。
我觉得我把事情复杂化了,遗漏了一些东西。
根据您的需求,我建议使用 Intent.ACTION_OPEN_DOCUMENT
。
1.创建照片选择 Intent
:
val REQUEST_CODE_PICK_PHOTO = 1
fun pickAndSavePhoto(requestCode: Int) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "image/*"
startActivityForResult(intent, requestCode)
}
2。处理结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_PICK_PHOTO && resultCode == RESULT_OK) {
val imageUri = data!!.data!!
//save this uri to your database as String -> imageUri.toString()
}
}
3。取回图像并显示在 ImageView
:
fun getBitmapFromUri(context: Context, imageUri: Uri): Bitmap? { //uri is just an address, image may be deleted any time, if so returns null
val bitmap: Bitmap
return try {
val inputStream = context.contentResolver.openInputStream(imageUri)
inputStream.use {
bitmap = BitmapFactory.decodeStream(it)
}
bitmap
} catch (e: Exception) {
Log.e("getBitmapFromUri()", "Image not found.")
null
}
}
val bitmap = getBitmapFromUri(context, imageUri) //get uri String from database and convert it to uri -> uriString.toUri()
if (bitmap != null) {
imageView.setImageBitmap(bitmap)
}
只有 ACTION_OPEN_DOCUMENT
可以永久访问文件 uri:
-
你确实把事情复杂化了。
将文件存储到文件系统中所需的文件夹中(最好以您的应用名称命名该文件夹)
存储此路径或 URI 路径 - 随心所欲。 (不要在您的应用程序中硬编码传递 - 设备供应商在其设备中可能有不同的基本路径)
只要文件夹名称相同且其中的文件名称相同(与您的数据库中的名称相同)- 即使取出 SD 卡然后再放回去,您也可以访问它们。
重建索引后可能会出现并发症 - 但在我作为 Android 开发人员工作的八年中,我只遇到过一次,因此您可以轻松忽略这些东西。
如果您想更好地控制您存储的内容并限制对其的访问 - 将数据存储到您应用程序的内部存储器中 - 这样您将 100% 确定数据的位置和它未被篡改。
从 Android10 开始,您有范围存储 - 它类似于内部存储,但它甚至可以在外部 sdcard 上。
Here 是对可能存储位置的简要概述。
不要想太多 - 它是 phone 的默认用例,它的工作方式正如您所期望的 - 非常好而且非常稳定。
首先需要在manifest中申请外部存储权限,同时申请Runtime Permission
在此目录中创建用于保存图像的目录后。
您还必须在 XML 和代码端添加文件提供程序,因为它是必需的。
现在是时候对我的代码进行代码检查,以将图像保存在文件夹中以及图库中的这些图像,并从文件路径中获取路径。
-
- 将 URI 转换为位图
-
- 从获取位图保存图片功能
私有字符串保存(位图位图){
File save_path = null;
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
try {
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/SaveDirectory");
dir.mkdirs();
File file = new File(dir, "DirName_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime()) + ".png");
save_path = file;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
FileOutputStream f = null;
f = new FileOutputStream(file);
MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
if (f != null) {
f.write(baos.toByteArray());
f.flush();
f.close();
}
} catch (Exception e) {
// TODO: handle exception
}
Share(save_path); // call your Function Store into database
Log.e("PathOFExec----", "save: " + save_path);
}
-
- 如果您愿意,可以将商店图像位置获取到您的数据库中
private void Share(文件保存路径){
if (savePath != null) {
Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", savePath);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_TEXT, "TextDetail");
share.putExtra(Intent.EXTRA_STREAM, uri);
context.startActivity(Intent.createChooser(share, "Share Image!"));
//after getting URI you can store the image into SQLite databse for get uri
}
}
我的目标是:
- 将媒体文件保存到外部存储(在我的例子中是照片)。
- 获取保存数据的文件路径或 URI。
- 将其保存到 SQLite(文件路径或内容 URI 或其他)。
- 能够在未来的任何时候获得此内容的正确 URI。
这与其他非常流行的应用程序所做的非常相似 - 他们在 'Pictures' 文件夹中创建目录并将照片存储在那里并在他们的应用程序中使用它们,同时也可以使用 [=45= 查看它们] 探险家等
据我所知,保存媒体内容(图片,f.e.)的推荐方法是使用 MediaStore
API 结果我得到了内容 URI,我可以使用它之后。
但后来我读到这些内容 URI 可能会在重新扫描媒体后发生更改,因此看起来不可靠。 (比如用了SD卡又拔出来再插)
同时不推荐使用绝对文件路径,并且有弃用使用绝对文件路径与外部存储一起使用的 APIs 的倾向。所以看起来也不靠谱
我只能想象以下解决方案:
- 保存时使用唯一的自动生成的文件名(如 UUID)。
- 当我需要获取内容 URI 时(f.e。想在
ImageView
中渲染照片)- 我可以使用ContentResolver
并使用文件名过滤器搜索内容 URI。
这种方法的问题是我有很多照片(图库),使用 ContentResolver
查询会显着影响性能。
我觉得我把事情复杂化了,遗漏了一些东西。
根据您的需求,我建议使用 Intent.ACTION_OPEN_DOCUMENT
。
1.创建照片选择 Intent
:
val REQUEST_CODE_PICK_PHOTO = 1
fun pickAndSavePhoto(requestCode: Int) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "image/*"
startActivityForResult(intent, requestCode)
}
2。处理结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_PICK_PHOTO && resultCode == RESULT_OK) {
val imageUri = data!!.data!!
//save this uri to your database as String -> imageUri.toString()
}
}
3。取回图像并显示在 ImageView
:
fun getBitmapFromUri(context: Context, imageUri: Uri): Bitmap? { //uri is just an address, image may be deleted any time, if so returns null
val bitmap: Bitmap
return try {
val inputStream = context.contentResolver.openInputStream(imageUri)
inputStream.use {
bitmap = BitmapFactory.decodeStream(it)
}
bitmap
} catch (e: Exception) {
Log.e("getBitmapFromUri()", "Image not found.")
null
}
}
val bitmap = getBitmapFromUri(context, imageUri) //get uri String from database and convert it to uri -> uriString.toUri()
if (bitmap != null) {
imageView.setImageBitmap(bitmap)
}
只有
ACTION_OPEN_DOCUMENT
可以永久访问文件 uri:
你确实把事情复杂化了。 将文件存储到文件系统中所需的文件夹中(最好以您的应用名称命名该文件夹)
存储此路径或 URI 路径 - 随心所欲。 (不要在您的应用程序中硬编码传递 - 设备供应商在其设备中可能有不同的基本路径)
只要文件夹名称相同且其中的文件名称相同(与您的数据库中的名称相同)- 即使取出 SD 卡然后再放回去,您也可以访问它们。
重建索引后可能会出现并发症 - 但在我作为 Android 开发人员工作的八年中,我只遇到过一次,因此您可以轻松忽略这些东西。
如果您想更好地控制您存储的内容并限制对其的访问 - 将数据存储到您应用程序的内部存储器中 - 这样您将 100% 确定数据的位置和它未被篡改。
从 Android10 开始,您有范围存储 - 它类似于内部存储,但它甚至可以在外部 sdcard 上。
Here 是对可能存储位置的简要概述。
不要想太多 - 它是 phone 的默认用例,它的工作方式正如您所期望的 - 非常好而且非常稳定。
首先需要在manifest中申请外部存储权限,同时申请Runtime Permission
在此目录中创建用于保存图像的目录后。
您还必须在 XML 和代码端添加文件提供程序,因为它是必需的。
现在是时候对我的代码进行代码检查,以将图像保存在文件夹中以及图库中的这些图像,并从文件路径中获取路径。
-
- 将 URI 转换为位图
-
- 从获取位图保存图片功能
私有字符串保存(位图位图){
File save_path = null; if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { try { File sdCard = Environment.getExternalStorageDirectory(); File dir = new File(sdCard.getAbsolutePath() + "/SaveDirectory"); dir.mkdirs(); File file = new File(dir, "DirName_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime()) + ".png"); save_path = file; ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); FileOutputStream f = null; f = new FileOutputStream(file); MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null); if (f != null) { f.write(baos.toByteArray()); f.flush(); f.close(); } } catch (Exception e) { // TODO: handle exception } Share(save_path); // call your Function Store into database Log.e("PathOFExec----", "save: " + save_path); }
-
- 如果您愿意,可以将商店图像位置获取到您的数据库中
private void Share(文件保存路径){
if (savePath != null) { Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", savePath); Intent share = new Intent(Intent.ACTION_SEND); share.setType("image/*"); share.putExtra(Intent.EXTRA_TEXT, "TextDetail"); share.putExtra(Intent.EXTRA_STREAM, uri); context.startActivity(Intent.createChooser(share, "Share Image!")); //after getting URI you can store the image into SQLite databse for get uri }
}