HB.*_*HB. 19 android android-11
根据 docs 文件路径访问权限在 Android R 中被授予:
从 Android 11 开始,具有 READ_EXTERNAL_STORAGE 权限的应用可以使用直接文件路径和本机库读取设备的媒体文件。这项新功能使您的应用程序可以更顺畅地与第三方媒体库配合使用。
问题是我无法从中获取文件路径MediaStore,那么我们应该如何读取无法访问/检索的文件路径?有没有办法,我不知道,我们可以从中获取文件路径MediaStore?
此外,文档说明如下:
所有文件访问
某些应用程序的核心用例需要广泛的文件访问,例如文件管理或备份和恢复操作。他们可以通过执行以下操作获得所有文件访问权限:
- 声明 MANAGE_EXTERNAL_STORAGE 权限。
- 将用户引导至系统设置页面,他们可以在其中为您的应用启用允许访问管理所有文件选项。
此权限授予以下权限:
- 对共享存储中所有文件的读访问和写访问。
- 访问 MediaStore.Files 表的内容。
但是我不需要所有文件访问权限,我只希望用户从中选择一个视频MediaStore并将文件路径传递给FFmpeg(它需要一个文件路径)。我知道我不能再使用该_data列来检索文件路径。
请注意:
Uri是从MediaStore文件中返回的,并且不指向文件。FFmpeg,但我可以在 Android R 之前做到这一点。FileDescriptor给FFmpeg我不能使用/proc/self/fd/(我/proc/7828/fd/70: Permission denied从 SD 卡中选择文件时得到),看看这个问题。那我该怎么办,我是不是错过了什么?是什么意思can read a device's media files using direct file paths and native libraries?
HB.*_*HB. 16
在对issuetracker提出问题后,我得出了以下结论:
File移除了在 Android Q 中添加的限制。所以我们可以再次访问File对象。如果您的目标是 Android 10 > 并且想要访问/使用文件路径,则必须在清单中添加/保留以下内容:
android:requestLegacyExternalStorage="true"
Run Code Online (Sandbox Code Playgroud)
这是为了确保文件路径在 Android 10(Q) 上运行。在 Android R 上,此属性将被忽略。
不要使用 DATA 列插入或更新到 Media Store,使用DISPLAY_NAMEand RELATIVE_PATH,这是一个例子:
android:requestLegacyExternalStorage="true"
Run Code Online (Sandbox Code Playgroud)你可以不再使用ACTION_OPEN_DOCUMENT_TREE或ACTION_OPEN_DOCUMENT意图行动,要求用户选择单个文件Android/data/,Android/obb/以及所有子目录。
File在需要执行“搜索”时使用对象FFmpeg,例如使用时。如果您想访问 aFile或想要从Uri返回的a 的文件路径MediaStore,我已经创建了一个库来处理您可能遇到的所有异常。这包括磁盘、内部和可移动磁盘上的所有文件。File例如,当从 Dropbox 中选择一个时,该文件File将被复制到您具有完全访问权限的应用程序目录中,然后将返回复制的文件路径。
Roh*_*nde 10
如果您的目标是 Android 11 API,则无法直接访问文件路径,因为 API 30(Android R) 中有很多限制。随着Android 10(API 29)中引入了作用域存储API,存储现在分为作用域存储(私有存储)和共享存储(公共存储)。范围存储是一种您只能访问在scoped storage directory(i.e. /Android/data/中创建的文件的存储 or /Android/media/<your-package-name>。您无法从共享存储(即内部存储/外部 SD 卡存储等)访问文件
共享存储再次进一步分为媒体和下载集合。媒体集合存储图像、音频和视频文件。下载集合将处理非媒体文件。
要了解有关作用域存储和共享存储的更多详细信息,请参阅此链接:Android 10 和 Android 11 中的作用域存储。
如果您正在处理媒体文件(即图像、视频、音频),您可以通过使用支持 API 30(Android 11) 的媒体存储 API 来获取文件路径。如果您正在处理非媒体文件(即文档和其他文件),您可以使用文件 Uri 获取文件路径。
注意:如果您使用文件或 Uri util 类(如 RealPathUtil、FilePathUtils 等)来获取文件路径,这里您可以获取所需的文件路径,但无法读取该文件,因为它会抛出Read异常在 Android 11 中访问(权限被拒绝),因为您无法读取其他应用程序创建的文件。
因此,要实现在 Android 11(API 30)中获取文件路径的这种场景,建议使用 File Uri 将文件复制到应用程序的缓存目录中,并从缓存目录中获取文件访问的路径。
在我的场景中,我使用了这两个 API 来获取 Android 11 中的文件访问权限。为了获取媒体文件(即图像、视频、音频)的文件路径,我使用了 Media Store API(请参阅此链接:Media存储 API 示例 - 从共享存储访问媒体文件),并获取非媒体文件(即文档和其他文件)的文件路径,我使用了 fileDescriptor。
文件描述符示例:我创建了系统对话框文件选择器来选择文件。
private fun openDocumentAction() {
val mimetypes = arrayOf(
"application/*", //"audio/*",
"font/*", //"image/*",
"message/*",
"model/*",
"multipart/*",
"text/*"
)
// you can customize the mime types as per your choice.
// Choose a directory using the system's file picker.
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
//type = "application/pdf" //only pdf files
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, mimetypes)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when it loads.
//putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, RC_SAF_NON_MEDIA)
}
Run Code Online (Sandbox Code Playgroud)
并在活动的onActivityResult方法中处理文件选择器的结果。在此处获取文件 URI。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
RC_SAF_NON_MEDIA -> {
//document selection by SAF(Storage Access Framework) for Android 11
if (resultCode == RESULT_OK) {
// The result data contains a URI for the document or directory that
// the user selected.
data?.data?.also { uri ->
//Permission needed if you want to retain access even after reboot
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
// Perform operations on the document using its URI.
val path = makeFileCopyInCacheDir(uri)
Log.e(localClassName, "onActivityResult: path ${path.toString()} ")
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
将文件 URI 传递给以下方法以获取文件路径。此方法将在应用程序的缓存目录中创建一个文件对象,从该位置您可以轻松地获得对该文件的读取访问权限。
private fun makeFileCopyInCacheDir(contentUri :Uri) : String? {
try {
val filePathColumn = arrayOf(
//Base File
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.TITLE,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.DISPLAY_NAME,
//Normal File
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.MIME_TYPE,
MediaStore.MediaColumns.DISPLAY_NAME
)
//val contentUri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(mediaUrl))
val returnCursor = contentUri.let { contentResolver.query(it, filePathColumn, null, null, null) }
if (returnCursor!=null) {
returnCursor.moveToFirst()
val nameIndex = returnCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
val name = returnCursor.getString(nameIndex)
val file = File(cacheDir, name)
val inputStream = contentResolver.openInputStream(contentUri)
val outputStream = FileOutputStream(file)
var read = 0
val maxBufferSize = 1 * 1024 * 1024
val bytesAvailable = inputStream!!.available()
//int bufferSize = 1024;
val bufferSize = Math.min(bytesAvailable, maxBufferSize)
val buffers = ByteArray(bufferSize)
while (inputStream.read(buffers).also { read = it } != -1) {
outputStream.write(buffers, 0, read)
}
inputStream.close()
outputStream.close()
Log.e("File Path", "Path " + file.path)
Log.e("File Size", "Size " + file.length())
return file.absolutePath
}
} catch (ex: Exception) {
Log.e("Exception", ex.message!!)
}
return contentUri.let { UriPathUtils().getRealPathFromURI(this, it).toString() }
}
Run Code Online (Sandbox Code Playgroud)
注意:您也可以使用此方法获取媒体文件(图像、视频、音频)和非媒体文件(文档和其他文件)的文件路径。只需要传递一个文件Uri即可。
为了获取路径,我将带有 fileDescriptor 的文件复制到新路径并使用该路径。
查找文件名:
private static String copyFileAndGetPath(Context context, Uri realUri, String id) {
final String selection = "_id=?";
final String[] selectionArgs = new String[]{id};
String path = null;
Cursor cursor = null;
try {
final String[] projection = {"_display_name"};
cursor = context.getContentResolver().query(realUri, projection, selection, selectionArgs,
null);
cursor.moveToFirst();
final String fileName = cursor.getString(cursor.getColumnIndexOrThrow("_display_name"));
File file = new File(context.getCacheDir(), fileName);
FileUtils.saveAnswerFileFromUri(realUri, file, context);
path = file.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return path;
}
Run Code Online (Sandbox Code Playgroud)
使用文件描述符复制:
fun saveAnswerFileFromUri(uri: Uri, destFile: File?, context: Context) {
try {
val pfd: ParcelFileDescriptor =
context.contentResolver.openFileDescriptor(uri, "r")!!
if (pfd != null) {
val fd: FileDescriptor = pfd.getFileDescriptor()
val fileInputStream: InputStream = FileInputStream(fd)
val fileOutputStream: OutputStream = FileOutputStream(destFile)
val buffer = ByteArray(1024)
var length: Int
while (fileInputStream.read(buffer).also { length = it } > 0) {
fileOutputStream.write(buffer, 0, length)
}
fileOutputStream.flush()
fileInputStream.close()
fileOutputStream.close()
pfd.close()
}
} catch (e: IOException) {
Timber.w(e)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17742 次 |
| 最近记录: |