将图片保存到共享图片文件夹 android target api 29

Posted

技术标签:

【中文标题】将图片保存到共享图片文件夹 android target api 29【英文标题】:saving picture to shared pictures folder android target api 29 【发布时间】:2020-04-19 22:11:09 【问题描述】:

我的目标是从相机应用程序请求图片,将其保存在我的应用程序数据中,并将相同的图片写入图库,因此如果用户删除应用程序,它仍然有拍摄的照片(与信使不同,我们的实用工具图片被认为对用户即使没有应用程序)。

我面临的问题与 29 API 级别的女巫紧密相关是构建目标(不接受降级)。

所以基本上我得到的是:

第一个问题:即使授予Manifest.permission.WRITE_EXTERNAL_STORAGE,我也无法访问使用Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 构造的文件,我得到IOException(权限被拒绝)

第二个问题contentResolver.insert(contentUri, contentValues) 在 pre Q 设备(诺基亚 8 android Pie)上抛出这个问题:

java.lang.SecurityException: Permission Denial: writing com.android.providers.media.MediaProvider uri content://media/external/images/media from .... requires android.permission.WRITE_EXTERNAL_STORAGE, or grantUriPermission()

如果我在 Android 10 设备(在我的情况下是模拟器)上运行,则使用 contentResolver.insert(..) 的选项可以正常工作。

主要问题当目标是 29Api 级别时,如何将图片正确写入 android 管理的画廊文件夹,以便它可以在 android 10 之前的设备上工作?

额外参考

我将图片从我的应用缓存存储写入 android 10 库的代码是:

    fun writePictureToGalleryQ(context: Context, pictureUri: Uri, pictureName: String) 

        val contentResolver = context.contentResolver

        val relativeLocation = "$Environment.DIRECTORY_PICTURES$File.separatorWaiJuDuDisAndroid"

        val contentValues  = ContentValues().apply 
            put(MediaStore.Images.ImageColumns.DISPLAY_NAME, pictureName)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
        

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

        uri = contentResolver.insert(contentUri, contentValues)
        uri?.let  galleryPartUri ->

            val outStream: OutputStream = contentResolver.openOutputStream(galleryPartUri)!!
            val inStream: InputStream = contentResolver.openInputStream(pictureUri)!!

            outStream.use out ->
                inStream.use  inpt ->
                    inpt.copyTo(out)
                
            
        
    

附:内容提供者在清单以及所需的权限和文件路径 xml 中设置。

应该为每个 Q android 版本执行的代码,但它看起来不像这样(假设我已经得到 Manifest.permission.WRITE_EXTERNAL_STORAGE

    fun writePictureToGalleryLegacy(context: Context, pictureUri: Uri, pictureName: String) 


        val directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)

        val correctDir = File("$directory.absolutePath$File.separatorWaiJuDuDisAndroid")
        correctDir.mkdirs()

        val file = File("$correctDir.absolutePath$File.separator$pictureUri.lastPathSegment")

        val contentResolver = context.contentResolver

        val outStream: OutputStream = file.outputStream()
        val inStream: InputStream = contentResolver.openInputStream(pictureUri)!!

        outStream.use out ->
            inStream.use  inpt ->
                inpt.copyTo(out)
            
        


        val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension)

        MediaScannerConnection.scanFile(
                context,
                arrayOf(file.absolutePath),
                arrayOf(mimeType?:"image/*"),
                null
        )

    

问题更新和编辑

我调整了我的代码块,使其更加正确,因为它们确实有效。

重建项目最终解决了我的问题。不知道我更讨厌什么——谷歌还是我自己。

当然,重建项目只是解决方案的一部分。

我遇到了两个问题:

    这里有些人删除了他的答案,说我可能缺少<application... android:requestLegacyExternalStorage="true" ....> 我的依赖项中的某些库的清单中有 android:maxSdkVersion,这基本上会导致忽略我对 Android 10 和 Android 9 设备以及可能从 Android 4.4 开始的所有设备的权限。

因此我更改了我的应用清单:

<manifest>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>

    <application android:requestLegacyExternalStorage="true" ...>
        ...
    </application>

</manifest>

【问题讨论】:

proandroiddev.com/working-with-scoped-storage-8a7e7cafea3 对于其余部分,不清楚您是否对 Q 或之前的 Q 有问题。 有了 Q 我没有问题。但是,当我尝试将相同的构建安装到 P 时。即使使用 android:requestLegacyExternalStorage="true",当使用 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 构建文件路径时,我也会得到 open failed: EACCES (Permission denied) 您可以在获得正确权限的情况下使用 Q 下面的路径。如果您想使用 MediaStore 插入文件或想要不使用,我们不清楚 Q 以下。为什么? Bellow Q 我正在尝试将图片直接写入文件“/storage/emulated/0/Pictures/PIC_1577795479840.jpeg”,但我收到带有评论“权限被拒绝”的异常。我想做什么 - 我想以任何可能的方式将图片保存到 android 图片目录或 DCIM。 【参考方案1】:

有同样的问题,将扩展 Cordova 应用程序的解决方案,最终对我有用的是将此行添加到 config.xml

<platform name="android">
   <custom-preference name="android-manifest/application/@android:requestLegacyExternalStorage" value="true" />

【讨论】:

以上是关于将图片保存到共享图片文件夹 android target api 29的主要内容,如果未能解决你的问题,请参考以下文章

java保存图片到本地服务器共享

android 如何获取保存的图片的地址 并存到数据库中

适配到Android 12,全版本支持保存图片到相册方案

Android 11 拍照+录制视频保存到外部共享区域

在 Android 上共享屏幕截图。每次共享同一张图片

在 Android 上的 Rhodes 中将签名图片保存到文件中