- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
好吧,我找了又找,没有人知道我的确切答案,或者我错过了。我让我的用户通过以下方式选择目录:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, READ_REQUEST_CODE);
在我的 Activity 中,我想捕捉实际路径,这似乎是不可能的。
protected void onActivityResult(int requestCode, int resultCode, Intent intent){
super.onActivityResult(requestCode, resultCode, intent);
if (resultCode == RESULT_OK) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
//Marshmallow
} else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
//Set directory as default in preferences
Uri treeUri = intent.getData();
//grant write permissions
getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
//File myFile = new File(uri.getPath());
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
我选择的文件夹在:
Device storage/test/
我已经尝试了以下所有方法来获取确切的路径名,但都无济于事。
File myFile = new File (uri.getPath());
//returns: /tree/1AF6-3708:test
treeUri.getPath();
//returns: /tree/1AF6-3708:test/
pickedDir.getName()
//returns: test
pickedDir.getParentFile()
//returns: null
基本上,我需要将 /tree/1AF6-3708:
转换为 /storage/emulated/0/
或每个设备称之为存储位置的任何内容。所有其他可用选项也返回 /tree/1AF6-37u08:
。
我想这样做有两个原因。
1) 在我的应用程序中,我将文件位置存储为共享首选项,因为它是特定于用户的。我有相当多的数据需要下载和存储,我希望用户能够将其放在他们想要的地方,尤其是当他们有额外的存储位置时。我确实设置了默认值,但我想要多功能性,而不是专用位置:
Device storage/Android/data/com.app.name/
2) 在 5.0 中,我想让用户获得对该文件夹的读/写权限,这似乎是唯一的方法。如果我可以从一个字符串中获得读/写权限,就可以解决这个问题。
我能找到的所有解决方案都与 Mediastore 相关,这对我没有任何帮助。我必须在某处丢失某些东西,或者我必须对它上釉。任何帮助,将不胜感激。谢谢。
最佳答案
这将为您提供所选文件夹的实际路径它仅适用于属于本地存储的文件/文件夹。
Uri treeUri = data.getData();
String path = FileUtil.getFullPathFromTreeUri(treeUri,this);
其中FileUtil是下面的类
public final class FileUtil {
private static final String PRIMARY_VOLUME_NAME = "primary";
@Nullable
public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
if (treeUri == null) return null;
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),con);
if (volumePath == null) return File.separator;
if (volumePath.endsWith(File.separator))
volumePath = volumePath.substring(0, volumePath.length() - 1);
String documentPath = getDocumentPathFromTreeUri(treeUri);
if (documentPath.endsWith(File.separator))
documentPath = documentPath.substring(0, documentPath.length() - 1);
if (documentPath.length() > 0) {
if (documentPath.startsWith(File.separator))
return volumePath + documentPath;
else
return volumePath + File.separator + documentPath;
}
else return volumePath;
}
private static String getVolumePath(final String volumeId, Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
return getVolumePathForAndroid11AndAbove(volumeId, context);
else
return getVolumePathBeforeAndroid11(volumeId, context);
}
private static String getVolumePathBeforeAndroid11(final String volumeId, Context context){
try {
StorageManager mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId)) // primary volume?
return (String) getPath.invoke(storageVolumeElement);
if (uuid != null && uuid.equals(volumeId)) // other volumes?
return (String) getPath.invoke(storageVolumeElement);
}
// not found.
return null;
} catch (Exception ex) {
return null;
}
}
@TargetApi(Build.VERSION_CODES.R)
private static String getVolumePathForAndroid11AndAbove(final String volumeId, Context context) {
try {
StorageManager mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
List<StorageVolume> storageVolumes = mStorageManager.getStorageVolumes();
for (StorageVolume storageVolume : storageVolumes) {
// primary volume?
if (storageVolume.isPrimary() && PRIMARY_VOLUME_NAME.equals(volumeId))
return storageVolume.getDirectory().getPath();
// other volumes?
String uuid = storageVolume.getUuid();
if (uuid != null && uuid.equals(volumeId))
return storageVolume.getDirectory().getPath();
}
// not found.
return null;
} catch (Exception ex) {
return null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getVolumeIdFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if (split.length > 0) return split[0];
else return null;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getDocumentPathFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if ((split.length >= 2) && (split[1] != null)) return split[1];
else return File.separator;
}
}
更新:
要解决评论中提到的下载问题:如果您从默认 Android 文件选择器的左侧抽屉中选择 Downloads
,您实际上并没有选择目录。下载是一个提供者。一个普通的文件夹树 uri 看起来像这样:
content://com.android.externalstorage.documents/tree/primary%3ADCIM
下载树的uri是
content://com.android.providers.downloads.documents/tree/downloads
你可以看到一个说 externalstorage
而另一个说 providers
。这就是为什么它无法与文件系统中的目录匹配的原因。因为它不是目录。
解决方案:您可以添加相等性检查,如果树 uri 等于它,则返回默认的下载文件夹路径,可以像这样检索它:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
如果您愿意,可以为所有提供者做类似的事情。我认为它大部分时间
都能正常工作。但我想在某些边缘情况下它不会。
感谢@DuhVir 支持 Android R 案例
关于来自树 URI 的 Android 5.0 DocumentFile,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34927748/
想象一下,你想检查“/folder/subfolder/subsubfolder/test/test.txt”文件是否存在,你会做如下: DocumentFile sdCard = ...; // i
如果我调用 DocumentFile.fromTreeUri()在主目录上,我列出了它的内容,它返回所有 DocumentFile与预期的目录中包含的文件夹和文件相关的对象,但是当我尝试调用 Docu
我尝试使用 DocumentFile (由于存储访问框架)在创建文件之前检查文件是否存在。但是DocumentFile().fromTreeUri()删除可能的 Uri 中不存在的部分,这会导致 Do
有没有办法从给定的 DocumentFile 中获取 RandomAccessFile? 我知道可以通过 getUri 获取 InputStream InputStream inputStream =
我正在尝试递归遍历 Android DocumentFile 树,但我无法获得要为子文件夹返回的文件列表。当我在文件夹 URI 上尝试 listFiles() 时,我每次都得到返回的根文件列表。 为什
对于Android,以下代码返回一个Uri,可用于创建与目录对应的DocumentFile。 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMEN
我在 Lollipop 遇到的情况是: 用户授予的目录树 (DocumentFile)。 从媒体提供商处检索到的需要修改的音频文件集合。 Uri mediaUri = Uri.withAppended
我使用 DocumentFile 检查文件元数据和存在性。这是这样实例化的: DocumentFile df = DocumentFile.fromSingleUri(context, uri); b
我正在尝试使基于File 的文档系统适应使用DocumentFile 的内容,以便允许在 API >= 29 上进行外部存储读/写访问。 我让用户使用 Intent.ACTION_OPEN_DOCUM
背景 从 API 21 (Lollipop) 开始,应用可以获得修改真实 SD 卡的特殊“权限”,如我之前写的帖子(here 和 here)所示。 问题 我可以删除文件,也可以创建文件,但是我找不到执
在我的应用程序中,用户选择一个目录,然后在 onActivityResult 中创建一个 DocumentFile,如下所示: Uri treeUri = resultData.getData();
Android API 有 DocumentFile class .这个类有canWrite() method . 假设我调用了这个方法,它返回了 true。还假设此对象代表“原始”文件。 现在我该怎
我有一个从 ACTION_OPEN_DOCUMENT_TREE 获得的树 URI , 如何在不使用 findFiles() 的情况下获得作为这棵树的子树的 DocumentFile ?假设我知道如何获
我想创建类似“PDF 查看器应用程序”的东西。应用程序将在用户选择的位置搜索所有 *.pdf 文件。用户可以通过这个函数选择这个文件夹: Intent intent = new Intent(Inte
我正在尝试使用 DocumentFile 在我的 android 5.1 手机上列出外部存储设备中的文件 String rootPathURI = "file:/media/storage/sdcar
我想将文件从内部存储复制或移动到 SD 卡。我通过存储访问框架 (SAF) 和 DocumentFile 类... 复制是基于流的,DocumentFile 没有像 File 类那样的功能来设置最后修
我需要访问 Android Studio 项目中的 DocumentFile 类。我花了很长时间才解决这个问题。 Andriod 开发人员文档说我需要导入“androidx.documentfile.
我想用 java.util.zip.ZipFile 读取一个 zip 文件与安卓 DocumentFile 插图: 使用 Storage Access Framework,我得到了 android 系
如果选择了设备的主内存,我需要从 DocumentFile 或 Uri 中获取一个具有正确方案的文件,而不是具有 content://com.android.externalstorage.docum
好吧,我找了又找,没有人知道我的确切答案,或者我错过了。我让我的用户通过以下方式选择目录: Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT
我是一名优秀的程序员,十分优秀!