存储访问框架获取正确的 Uri 路径 Delete/Edit/Get 文件
Storage Access Framework Getting Correct Uri Path Delete/Edit/Get File
TL:DR; 我解释了如何使用 DocumentFile 创建文件夹和子文件夹以及如何删除使用此 class 创建的文件。从 onActvityResult() 和 documentFile.getUri.toString() 返回的 Uri 不相同。我的问题是如何在不使用 SAF UI 的情况下获得有效的 Uri 来操作文件夹和文件,如果可能的话,不使用 hack。
让我分享一下我到目前为止学到的知识并提出我的问题。
如果你想获取文件夹的 Uri 并对其进行处理,你应该使用 Intent
和 ACTION_OPEN_DOCUMENT_TREE
来获取 Uri 以访问文件夹并为该 uri 设置 W/R 权限。
已授予持久权限 onActivityResult with:
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
如果您select设备的主文件夹:
Uri treeUri = data.getData();
treeUri.toString()
Returns: content://com.android.externalstorage.documents/tree/primary:
File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), "");
Returns: storage/emulated/0
new File(treeUri.toString()).getAbsolutePath();
Returns:内容:/com.android.externalstorage.documents/tree/primary:
如果您使用 DocumentFile class 获取主文件夹的路径,您会得到
DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
String uriString = saveDir.getUri().toString();
Returns: 文件:///storage/emulated/0
我的第一个问题是如何使用 DocumentFile class.
获取带有内容的 Uri
我正在开发ui一个摄影应用程序,默认情况下我想使用 DocumentFile class.
为图像设置一个初始文件夹
@TargetApi(19)
protected DocumentFile getSaveDirMainMemory() {
DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
// saveDir =
// DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
// saveDir =
// DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
DocumentFile newDir = null;
/*
* Check or create Main Folder
*/
// Check if main folder exist
newDir = saveDir.findFile(DIR_MAIN);
// Folder does not exist, create it
if (newDir == null || !newDir.exists()) {
newDir = saveDir.createDirectory(DIR_MAIN);
}
/*
* Check or create Sub-Folder
*/
DocumentFile newSubDir = null;
// Check if sub-folder exist
newSubDir = newDir.findFile(DIR_SUB);
// Folder does not exist, create it
if (newSubDir == null || !newSubDir.exists()) {
newSubDir = newDir.createDirectory(DIR_SUB);
}
if (newSubDir != null && newSubDir.exists()) {
return newSubDir;
} else if (newDir != null && newDir.exists()) {
return newDir;
} else {
return saveDir;
}
}
此方法根据选择在设备的主内存或 PICTURES 或 DCIM 文件夹中创建 DIR_MAIN/DIR_SUB。使用此默认文件夹,我将图像保存到此创建的子文件夹中。
我得到 newSubDir.getUri().toString(): file:///storage/emulated/0/MainFolder/SubFolder 我命名为 DIR_MAIN MainFolder, DIR_SUB: 要测试的子文件夹。
要访问或删除图像,我使用此路径和我创建的图像名称
DocumentFile imageToDeletePath = DocumentFile.fromFile(new File(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);
imageDelete returns null 因为 Uri 的格式不正确。
如果我打开 SAF ui 并获取 UI onActivityResult 并将其保存为字符串,我将使用此方法获取目录并检查 Uri 权限
@TargetApi(19)
protected DocumentFile getSaveDirNew(String uriString) {
DocumentFile saveDir = null;
boolean canWrite = isUriWritePermission(uriString);
if (canWrite) {
try {
saveDir = DocumentFile.fromTreeUri(MainActivity.this, Uri.parse(uriString));
} catch (Exception e) {
saveDir = null;
}
}
return saveDir;
}
检查字符串中的 Uri 是否具有写权限,如果您不获取或删除持久权限,它可能没有。
private boolean isUriWritePermission(String uriString) {
boolean canWrite = false;
List<UriPermission> perms = getContentResolver().getPersistedUriPermissions();
for (UriPermission p : perms) {
if (p.getUri().toString().equals(uriString) && p.isWritePermission()) {
Toast.makeText(this, "canWrite() can write URI:: " + p.getUri().toString(), Toast.LENGTH_LONG).show();
canWrite = true;
break;
}
}
return canWrite;
}
使用有效 uri 保存图像并使用后
DocumentFile imageToDeletePath = DocumentFile.fromTreeUri(this, Uri.parse(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);
Uri.fromFile()
和 DocumentFile.fromTreeUri()
从两个不同的世界创建 uris。
虽然它们目前看起来非常相似,但这只是巧合,可能会随着未来 Android 版本的发布而改变。
没有 "non-hacky" 方法可以从一种转换为另一种。如果你想要一个肮脏的解决方案,你可以去反思(查看 DocumentFile.fromTreeUri
的源代码,并可能在较新的 Android 版本上使用 Storage
class。
另见:
TL:DR; 我解释了如何使用 DocumentFile 创建文件夹和子文件夹以及如何删除使用此 class 创建的文件。从 onActvityResult() 和 documentFile.getUri.toString() 返回的 Uri 不相同。我的问题是如何在不使用 SAF UI 的情况下获得有效的 Uri 来操作文件夹和文件,如果可能的话,不使用 hack。
让我分享一下我到目前为止学到的知识并提出我的问题。
如果你想获取文件夹的 Uri 并对其进行处理,你应该使用 Intent
和 ACTION_OPEN_DOCUMENT_TREE
来获取 Uri 以访问文件夹并为该 uri 设置 W/R 权限。
已授予持久权限 onActivityResult with:
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
如果您select设备的主文件夹:
Uri treeUri = data.getData();
treeUri.toString()
Returns: content://com.android.externalstorage.documents/tree/primary:
File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), "");
Returns: storage/emulated/0
new File(treeUri.toString()).getAbsolutePath();
Returns:内容:/com.android.externalstorage.documents/tree/primary:
如果您使用 DocumentFile class 获取主文件夹的路径,您会得到
DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
String uriString = saveDir.getUri().toString();
Returns: 文件:///storage/emulated/0
我的第一个问题是如何使用 DocumentFile class.
获取带有内容的 Uri我正在开发ui一个摄影应用程序,默认情况下我想使用 DocumentFile class.
为图像设置一个初始文件夹 @TargetApi(19)
protected DocumentFile getSaveDirMainMemory() {
DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
// saveDir =
// DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
// saveDir =
// DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
DocumentFile newDir = null;
/*
* Check or create Main Folder
*/
// Check if main folder exist
newDir = saveDir.findFile(DIR_MAIN);
// Folder does not exist, create it
if (newDir == null || !newDir.exists()) {
newDir = saveDir.createDirectory(DIR_MAIN);
}
/*
* Check or create Sub-Folder
*/
DocumentFile newSubDir = null;
// Check if sub-folder exist
newSubDir = newDir.findFile(DIR_SUB);
// Folder does not exist, create it
if (newSubDir == null || !newSubDir.exists()) {
newSubDir = newDir.createDirectory(DIR_SUB);
}
if (newSubDir != null && newSubDir.exists()) {
return newSubDir;
} else if (newDir != null && newDir.exists()) {
return newDir;
} else {
return saveDir;
}
}
此方法根据选择在设备的主内存或 PICTURES 或 DCIM 文件夹中创建 DIR_MAIN/DIR_SUB。使用此默认文件夹,我将图像保存到此创建的子文件夹中。 我得到 newSubDir.getUri().toString(): file:///storage/emulated/0/MainFolder/SubFolder 我命名为 DIR_MAIN MainFolder, DIR_SUB: 要测试的子文件夹。
要访问或删除图像,我使用此路径和我创建的图像名称
DocumentFile imageToDeletePath = DocumentFile.fromFile(new File(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);
imageDelete returns null 因为 Uri 的格式不正确。
如果我打开 SAF ui 并获取 UI onActivityResult 并将其保存为字符串,我将使用此方法获取目录并检查 Uri 权限
@TargetApi(19)
protected DocumentFile getSaveDirNew(String uriString) {
DocumentFile saveDir = null;
boolean canWrite = isUriWritePermission(uriString);
if (canWrite) {
try {
saveDir = DocumentFile.fromTreeUri(MainActivity.this, Uri.parse(uriString));
} catch (Exception e) {
saveDir = null;
}
}
return saveDir;
}
检查字符串中的 Uri 是否具有写权限,如果您不获取或删除持久权限,它可能没有。
private boolean isUriWritePermission(String uriString) {
boolean canWrite = false;
List<UriPermission> perms = getContentResolver().getPersistedUriPermissions();
for (UriPermission p : perms) {
if (p.getUri().toString().equals(uriString) && p.isWritePermission()) {
Toast.makeText(this, "canWrite() can write URI:: " + p.getUri().toString(), Toast.LENGTH_LONG).show();
canWrite = true;
break;
}
}
return canWrite;
}
使用有效 uri 保存图像并使用后
DocumentFile imageToDeletePath = DocumentFile.fromTreeUri(this, Uri.parse(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);
Uri.fromFile()
和 DocumentFile.fromTreeUri()
从两个不同的世界创建 uris。
虽然它们目前看起来非常相似,但这只是巧合,可能会随着未来 Android 版本的发布而改变。
没有 "non-hacky" 方法可以从一种转换为另一种。如果你想要一个肮脏的解决方案,你可以去反思(查看 DocumentFile.fromTreeUri
的源代码,并可能在较新的 Android 版本上使用 Storage
class。
另见: