gpt4 book ai didi

java - 如何使用 MediaStore 在 Android Q 中保存图像?

转载 作者:塔克拉玛干 更新时间:2023-11-02 22:59:51 25 4
gpt4 key购买 nike

这是新 Android Q 的链接 Scoped Storage .

根据 this Android Developers Best Practices Blog , 存储共享媒体文件(这是我的情况)应该使用 MediaStore 来完成API。

深入研究文档,我找不到相关函数。

这是我在 Kotlin 中的试用:

val bitmap = getImageBitmap() // I have a bitmap from a function or callback or whatever
val name = "example.png" // I have a name

val picturesDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!

// Make sure the directory "Android/data/com.mypackage.etc/files/Pictures" exists
if (!picturesDirectory.exists()) {
picturesDirectory.mkdirs()
}

try {
val out = FileOutputStream(File(picturesDirectory, name))
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)

out.flush()
out.close()

} catch(e: Exception) {
// handle the error
}

结果是我的图片保存在这里 Android/data/com.mypackage.etc/files/Pictures/example.png 如最佳实践博客中所述 Storing app-内部文件


我的问题是:

如何使用 MediaStore API 保存图像?


Java 答案同样可以接受。

提前致谢!


编辑

感谢 PerracoLabs

这真的很有帮助!

但是还有3点。

这是我的代码:

val name = "Myimage"
val relativeLocation = Environment.DIRECTORY_PICTURES + File.pathSeparator + "AppName"

val contentValues = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")

// without this part causes "Failed to create new MediaStore record" exception to be invoked (uri is null below)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
}
}

val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
var stream: OutputStream? = null
var uri: Uri? = null

try {
uri = contentResolver.insert(contentUri, contentValues)
if (uri == null)
{
throw IOException("Failed to create new MediaStore record.")
}

stream = contentResolver.openOutputStream(uri)

if (stream == null)
{
throw IOException("Failed to get output stream.")
}

if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream))
{
throw IOException("Failed to save bitmap.")
}


Snackbar.make(mCoordinator, R.string.image_saved_success, Snackbar.LENGTH_INDEFINITE).setAction("Open") {
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_VIEW
intent.data = contentUri
startActivity(Intent.createChooser(intent, "Select Gallery App"))
}.show()

} catch(e: IOException) {
if (uri != null)
{
contentResolver.delete(uri, null, null)
}

throw IOException(e)

}
finally {
stream?.close()
}

1- 保存的图像没有得到正确的名称“Myimage.png”

我尝试使用“Myimage”和“Myimage.PNG”,但均无效。

图像总是有一个由数字组成的名称,例如:

1563468625314.jpg

这给我们带来了第二个问题:

2- 即使我将位图压缩为 png 格式,图像仍保存为 jpg

不是什么大问题。只是好奇为什么。

3- relativeLocation 位在低于 Android Q 的设备上导致异常。在用“Android Version Check”if 语句包围后,图像直接保存在 Pictures 文件夹的根目录中。

再次感谢。


编辑 2

更改为:

uri = contentResolver.insert(contentUri, contentValues)
if (uri == null)
{
throw IOException("Failed to create new MediaStore record.")
}

val cursor = contentResolver.query(uri, null, null, null, null)
DatabaseUtils.dumpCursor(cursor)
cursor!!.close()

stream = contentResolver.openOutputStream(uri)

这是日志

I/System.out: >>>>> Dumping cursor android.content.ContentResolver$CursorWrapperInner@76da9d1
I/System.out: 0 {
I/System.out: _id=25417
I/System.out: _data=/storage/emulated/0/Pictures/1563640732667.jpg
I/System.out: _size=null
I/System.out: _display_name=Myimage
I/System.out: mime_type=image/png
I/System.out: title=1563640732667
I/System.out: date_added=1563640732
I/System.out: is_hdr=null
I/System.out: date_modified=null
I/System.out: description=null
I/System.out: picasa_id=null
I/System.out: isprivate=null
I/System.out: latitude=null
I/System.out: longitude=null
I/System.out: datetaken=null
I/System.out: orientation=null
I/System.out: mini_thumb_magic=null
I/System.out: bucket_id=-1617409521
I/System.out: bucket_display_name=Pictures
I/System.out: width=null
I/System.out: height=null
I/System.out: is_hw_privacy=null
I/System.out: hw_voice_offset=null
I/System.out: is_hw_favorite=null
I/System.out: hw_image_refocus=null
I/System.out: album_sort_index=null
I/System.out: bucket_display_name_alias=null
I/System.out: is_hw_burst=0
I/System.out: hw_rectify_offset=null
I/System.out: special_file_type=0
I/System.out: special_file_offset=null
I/System.out: cam_perception=null
I/System.out: cam_exif_flag=null
I/System.out: }
I/System.out: <<<<<

我注意到 title 与名称匹配,所以我尝试添加:

put(MediaStore.Images.ImageColumns.TITLE, 名称)

它仍然没有工作,这里是新的日志:

I/System.out: >>>>> Dumping cursor android.content.ContentResolver$CursorWrapperInner@51021a5
I/System.out: 0 {
I/System.out: _id=25418
I/System.out: _data=/storage/emulated/0/Pictures/1563640934803.jpg
I/System.out: _size=null
I/System.out: _display_name=Myimage
I/System.out: mime_type=image/png
I/System.out: title=Myimage
I/System.out: date_added=1563640934
I/System.out: is_hdr=null
I/System.out: date_modified=null
I/System.out: description=null
I/System.out: picasa_id=null
I/System.out: isprivate=null
I/System.out: latitude=null
I/System.out: longitude=null
I/System.out: datetaken=null
I/System.out: orientation=null
I/System.out: mini_thumb_magic=null
I/System.out: bucket_id=-1617409521
I/System.out: bucket_display_name=Pictures
I/System.out: width=null
I/System.out: height=null
I/System.out: is_hw_privacy=null
I/System.out: hw_voice_offset=null
I/System.out: is_hw_favorite=null
I/System.out: hw_image_refocus=null
I/System.out: album_sort_index=null
I/System.out: bucket_display_name_alias=null
I/System.out: is_hw_burst=0
I/System.out: hw_rectify_offset=null
I/System.out: special_file_type=0
I/System.out: special_file_offset=null
I/System.out: cam_perception=null
I/System.out: cam_exif_flag=null
I/System.out: }
I/System.out: <<<<<

而且我无法将 date_added 更改为名称。

并且 MediaStore.MediaColumns.DATA 已弃用。

提前致谢!

最佳答案

尝试下一个方法。如果文件夹不存在,Android Q(及更高版本)已经负责创建文件夹。该示例被硬编码为输出到 DCIM 文件夹中。如果您需要一个子文件夹,请将子文件夹名称附加为下一个:

final String relativeLocation = Environment.DIRECTORY_DCIM + File.separator + “YourSubforderName”;

考虑压缩格式应该和mime-type参数有关。例如,对于 JPEG 压缩格式,mime 类型将是“image/jpeg”,等等。可能您还想将压缩质量作为参数传递,在此示例中硬编码为 95。

Java:

@NonNull
public Uri saveBitmap(@NonNull final Context context, @NonNull final Bitmap bitmap,
@NonNull final Bitmap.CompressFormat format,
@NonNull final String mimeType,
@NonNull final String displayName) throws IOException {

final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);

final ContentResolver resolver = context.getContentResolver();
Uri uri = null;

try {
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, values);

if (uri == null)
throw new IOException("Failed to create new MediaStore record.");

try (final OutputStream stream = resolver.openOutputStream(uri)) {
if (stream == null)
throw new IOException("Failed to open output stream.");

if (!bitmap.compress(format, 95, stream))
throw new IOException("Failed to save bitmap.");
}

return uri;
}
catch (IOException e) {

if (uri != null) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}

throw e;
}
}

Kotlin

@Throws(IOException::class)
fun saveBitmap(
context: Context, bitmap: Bitmap, format: Bitmap.CompressFormat,
mimeType: String, displayName: String
): Uri {

val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
}

val resolver = context.contentResolver
var uri: Uri? = null

try {
uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
?: throw IOException("Failed to create new MediaStore record.")

resolver.openOutputStream(uri)?.use {
if (!bitmap.compress(format, 95, it))
throw IOException("Failed to save bitmap.")
} ?: throw IOException("Failed to open output stream.")

return uri

} catch (e: IOException) {

uri?.let { orphanUri ->
// Don't leave an orphan entry in the MediaStore
resolver.delete(orphanUri, null, null)
}

throw e
}
}

Kotlin 变体,具有更实用的风格:

@Throws(IOException::class)
fun saveBitmap(
context: Context, bitmap: Bitmap, format: Bitmap.CompressFormat,
mimeType: String, displayName: String
): Uri {

val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
}

var uri: Uri? = null

return runCatching {
with(context.contentResolver) {
insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)?.also {
uri = it // Keep uri reference so it can be removed on failure

openOutputStream(it)?.use { stream ->
if (!bitmap.compress(format, 95, stream))
throw IOException("Failed to save bitmap.")
} ?: throw IOException("Failed to open output stream.")

} ?: throw IOException("Failed to create new MediaStore record.")
}
}.getOrElse {
uri?.let { orphanUri ->
// Don't leave an orphan entry in the MediaStore
context.contentResolver.delete(orphanUri, null, null)
}

throw it
}
}

关于java - 如何使用 MediaStore 在 Android Q 中保存图像?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56904485/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com