如何使用MediaStore在Android Q中保存图像? [英] How to save an image in Android Q using MediaStore?

查看:156
本文介绍了如何使用MediaStore在Android Q中保存图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里是新的Android Q 范围存储的链接.

Here is a link to the new Android Q Scoped Storage.

根据此Android开发者最佳实践博客storing shared media files(这是我的情况),应使用 MediaStore API.

According to this Android Developers Best Practices Blog, storing shared media files (which is my case) should be done using the MediaStore API.

浏览文档后,我找不到相关功能.

Digging into the docs and I cannot find a relevant function.

这是我在科特林的审判:

Here is my trial in 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-internal files

The result is that my image is saved here Android/data/com.mypackage.etc/files/Pictures/example.png as described in the Best Practices Blog as Storing app-internal files

我的问题是:

如何使用MediaStore API保存图像?

How to save an image using the MediaStore API?

同样可以接受Java中的答案.

Answers in Java are equally acceptable.

预先感谢!

编辑

感谢PerracoLabs

Thanks to 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"

1- The image saved doesn't get its correct name "Myimage.png"

我尝试使用"Myimage"和"Myimage.PNG",但是都没有.

I tried using "Myimage" and "Myimage.PNG" but neither worked.

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

The image always gets a name made up of numbers like:

1563468625314.jpg

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

Which bring us to the second problem:

2-即使我以png的格式压缩位图,图像也保存为jpg.

2- The image is saved as jpg even though I compress the bitmap in the format of png.

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

Not a big issue. Just curious why.

3-相对位置位导致设备小于Android Q的异常. 用"Android Version Check" if语句包围后,图像将直接保存在Pictures文件夹的根目录中.

3- The relativeLocation bit causes an exception on Devices less than Android Q. After surrounding with the "Android Version Check" if statement, the images are saved directly in the root of the Pictures folder.

另一个谢谢你.

编辑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与名称匹配,所以我尝试添加:

I noticed the title to be matching the name so I tried adding:

put(MediaStore.Images.ImageColumns.TITLE, name)

它仍然不起作用,这是新的日志:

It still didn't work and here are the new logs:

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更改为名称.

And I can't change date_added to a name.

并且不推荐使用MediaStore.MediaColumns.DATA.

预先感谢!

推荐答案

尝试下一种方法.如果不存在,Android Q已经负责创建文件夹.该示例经过硬编码后可以输出到Pictures文件夹中.如果需要子文件夹,请在子文件夹名称后添加下一个名称:

Try the next method. Android Q already takes care of creating the folders if they don’t exist. The example is hard-coded to output into the Pictures folder. If you need a sub-folder then append the sub-folder name as next:

final String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator + "YourSubforderName";

请考虑压缩格式应与mime-type参数相关.例如,对于JPEG压缩格式,mime类型将为"image/jpeg",依此类推.

Consider that the compress format should be related to the mime-type parameter. For example, with a JPEG compress format the mime-type would be "image/jpeg", and so on.

private void 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 String relativeLocation = Environment.DIRECTORY_PICTURES;

    final ContentValues  contentValues = new ContentValues();
    contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
    contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);

    final ContentResolver resolver = context.getContentResolver();

    OutputStream stream = null;
    Uri uri = null;

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

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

        stream = resolver.openOutputStream(uri);

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

        if (bitmap.compress(format, 95, stream) == false)
        {
            throw new IOException("Failed to save bitmap.");
        }
    }
    catch (IOException e)
    {
        if (uri != null)
        {
            // Don't leave an orphan entry in the MediaStore
            resolver.delete(uri, null, null);
        }

        throw e;
    }
    finally
    {
        if (stream != null)
        {
            stream.close();
        }
    }
}

这篇关于如何使用MediaStore在Android Q中保存图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆