将图片保存到共享图片文件夹 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的主要内容,如果未能解决你的问题,请参考以下文章