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

Oma*_*fiz 13 java android image mediastore kotlin

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

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

深入研究文档,我找不到相关功能。

这是我在科特林的审判:

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
}
Run Code Online (Sandbox Code Playgroud)

结果是,Android/data/com.mypackage.etc/files/Pictures/example.png按照最佳做法博客中的描述,我的图像保存在此处为Storing app-internal files


我的问题是:

如何使用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()
}
Run Code Online (Sandbox Code Playgroud)

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

我尝试使用“ Myimage”和“ Myimage.PNG”,但是都没有用。

图像始终具有由数字组成的名称,例如:

1563468625314.jpg

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

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

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

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)
Run Code Online (Sandbox Code Playgroud)

这是日志

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: <<<<<
Run Code Online (Sandbox Code Playgroud)

我注意到该title名称与之匹配,因此我尝试添加:

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

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

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: <<<<<
Run Code Online (Sandbox Code Playgroud)

而且我不能改date_added名字。

并且MediaStore.MediaColumns.DATA已弃用。

提前致谢!

Rac*_*era 42

private void saveImage(Bitmap bitmap, @NonNull String name) throws IOException {
    OutputStream fos;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        ContentResolver resolver = getContentResolver();
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name + ".jpg");
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
        contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
        Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
    } else {
        String imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
        File image = new File(imagesDir, name + ".jpg");
        fos = new FileOutputStream(image);
    }
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
    Objects.requireNonNull(fos).close();
}
Run Code Online (Sandbox Code Playgroud)

图像将存储在图片文件夹@根级别

现场观看https://youtu.be/695HqaiwzQ0我创建的教程

  • @RachitVohera您的答案是正确的,但您之前的评论不是 - Android 10中不推荐使用`getExternalStoragePublicDirectory`。这就是您在答案中使用`if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.Q) {`的原因。 (5认同)
  • 您可以使用以下代码检查是否存在文件 `if(isFilePresent()){ // 在此处调用保存函数 }else{ // Toast 文件是否存在 // } private boolean isFilePresent() { File file = new File(Environment.getExternalStoragePublicDirectory(Environment) .DIRECTORY_PICTURES), 名称 + ".jpg");return file.exists(); }` (2认同)

小智 9

这是我经常使用的。你可以试试看。

 private void saveImageToStorage() throws IOException {

    OutputStream imageOutStream;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, "image_screenshot.jpg");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

        imageOutStream = getContentResolver().openOutputStream(uri);
    } else {
        String imagePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
        File image = new File(imagePath, "image_screenshotjpg");
        imageOutStream = new FileOutputStream(image);
    }

    try {
        bitmapObject.compress(Bitmap.CompressFormat.JPEG, 100, imageOutStream);
    } finally {
        imageOutStream.close();
    }

}
Run Code Online (Sandbox Code Playgroud)


Per*_*abs 8

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

final String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator + “YourSubforderName”;
Run Code Online (Sandbox Code Playgroud)

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

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)
        {
            resolver.delete(uri, null, null);
        }

        throw e;
    }
    finally
    {
        if (stream != null)
        {
            stream.close();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)