获取Marshmallow系统中SD卡文件的URI的真实路径

8

我希望能够选择存在于SD卡中而非内部存储中的文件,并将其上传至服务器,但是我无法获取它的路径以获取其大小。我已经使用以下代码启动了一个意图来选择文件:

 intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
 intent.addCategory(Intent.CATEGORY_OPENABLE);
 intent.setType("*/*");
 String[] mimetypes = {"application/*"};
 intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
 startActivityForResult(
      Intent.createChooser(intent, "Select a File to Upload"), FILE_SELECT_CODE);

为了获取文件路径,我使用这个答案,它可以正常工作,除非用户从sdcard(可移动设备)中选择任何文件。当我调试代码时发现类型不是primary,所以它不会进入此条件:
if("primary".equalsIgnoreCase(type)){
     return Environment.getExternalStorageDirectory() + "/" + split[1];
} 

我的问题是如果类型不是主要的,那么它会是什么?在这种情况下我们如何获取文件路径?我搜索了很多问题和教程,但没有一个提到else。我也尝试了这个答案的else部分,但它不起作用,因为System.getenv()返回null(对于“SECONDARY_STORAGE”)和sdcard(对于“EXTERNAL_STORAGE”)。当我尝试时,我遇到了文件未找到异常:

if ("primary".equalsIgnoreCase(type)) {
    return Environment.getExternalStorageDirectory() + "/" + split[1];
}else{
    return System.getenv("EXTERNAL_STORAGE") + "/" + split[1];
}

文件的Uri和Doc Id看起来像这样:

Uri: content://com.android.externalstorage.documents/document/0EF9-3110%3Adevice-2016-12-02-130553.png

docId: 0EF9-3110:device-2016-12-02-130553.png

需要帮助吗?


你的问题已经被报告很多次了。我想过去几个月每周都有报告。所以请先阅读这个网站。此外,您应该解释为什么需要文件路径。您大多数情况下可以使用您已有的内容方案来完成相同的操作。 - greenapps
@greenapps 我已经搜索过并且已经发布了链接..如果你有任何与我的问题相关的链接,请分享一下。 - Jaiprakash Soni
@greenapps 立即检查 - Jaiprakash Soni
上传文件到服务器时,您不需要使用文件系统路径,可以使用其URI!调整您的代码以适应现代化!请展示您上传代码的部分。 - greenapps
@JaiprakashSoni 你会选择图片还是文件? - Farmer
显示剩余5条评论
3个回答

14

在花时间使用Android设备管理器后,我找到了解决方案,如下所示:

如果文档类型ID不是主要的,则我使用以下方法创建路径:

filePath = "/storage/" + type + "/" + split[1];

编辑1: 如果是DocumentUri,则根据文件类型选择contentUri。

这是完整的函数:

public static String getRealPathFromURI_API19(Context context, Uri uri) {
    String filePath = "";

    // ExternalStorageProvider
    if (isExternalStorageDocument(uri)) {
        final String docId = DocumentsContract.getDocumentId(uri);
        final String[] split = docId.split(":");
        final String type = split[0];

        if ("primary".equalsIgnoreCase(type)) {
            return Environment.getExternalStorageDirectory() + "/" + split[1];
        } else {

            if (Build.VERSION.SDK_INT > 20) {
                    //getExternalMediaDirs() added in API 21
                    File extenal[] = context.getExternalMediaDirs();
                   for (File f : extenal) {
                    filePath = f.getAbsolutePath();
                    if (filePath.contains(type)) {
                        int endIndex = filePath.indexOf("Android");
                        filePath = filePath.substring(0, endIndex) + split[1];
                    }
                }
             }else{
                    filePath = "/storage/" + type + "/" + split[1];
             }
            return filePath;
        }

    } else if (isDownloadsDocument(uri)) {
        // DownloadsProvider
        final String id = DocumentsContract.getDocumentId(uri);
        //final Uri contentUri = ContentUris.withAppendedId(
        // Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {column};

        try {
            cursor = context.getContentResolver().query(uri, projection, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                String result = cursor.getString(index);
                cursor.close();
                return result;
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
    } else if (DocumentsContract.isDocumentUri(context, uri)) {
        // MediaProvider
        String wholeID = DocumentsContract.getDocumentId(uri);

        // Split at colon, use second item in the array
        String[] ids = wholeID.split(":");
        String id;
        String type;
        if (ids.length > 1) {
            id = ids[1];
            type = ids[0];
        } else {
            id = ids[0];
            type = ids[0];
        }

        Uri contentUri = null;
        if ("image".equals(type)) {
            contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        } else if ("video".equals(type)) {
            contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
        } else if ("audio".equals(type)) {
            contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        }

        final String selection = "_id=?";
        final String[] selectionArgs = new String[]{id};
        final String column = "_data";
        final String[] projection = {column};
        Cursor cursor = context.getContentResolver().query(contentUri, 
            projection, selection, selectionArgs, null);

        if (cursor != null) {
            int columnIndex = cursor.getColumnIndex(column);

            if (cursor.moveToFirst()) {
                filePath = cursor.getString(columnIndex);
            }
            cursor.close();
        }
        return filePath;
    } else {
        String[] proj = {MediaStore.Audio.Media.DATA};
        Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
        if (cursor != null) {
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
            if (cursor.moveToFirst())
                filePath = cursor.getString(column_index);
            cursor.close();
        }


        return filePath;
    }
    return null;
}

编辑2:对于像content://com.adobe.scan.android.documents/document/这样的主机进行处理,请查看此处的代码here


对于方法“isExternalStorageDocument”和“isDownloadsDocument”,我该怎么办? - M at
2
您可以在此处找到这些函数 https://dev59.com/klwX5IYBdhLWcg3w2iku#33298265 - Jaiprakash Soni

3

更改您的上传代码。某个地方你会有

FileInputStream fis = new FileInputStream(path);

改为

InputStream is = getContentResolver().openInputStream(uri);

所以直接使用URI即可,无需文件路径。

0

在您的应用程序中实现FileProvider非常容易。首先,您需要在AndroidManifest.xml文件中添加一个FileProvider标签,放在标签下面,如下所示: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

然后在res文件夹下的xml文件夹中创建一个provider_paths.xml文件。如果不存在该文件夹,则需要创建。

res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

完成了!FileProvider现在已经声明并准备好使用。

最后一步是更改MainActivity.java中下面的代码行:

Uri photoURI = Uri.fromFile(createImageFile());
to



 Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
            BuildConfig.APPLICATION_ID + ".provider",
            createImageFile());

完成!你的应用现在应该在包括 Android Nougat 在内的任何 Android 版本上都能够完美地运行。耶!


你能详细解释一下你的答案吗?为什么我需要在我的AndroidManifest中添加这个FileProvider? - Jaiprakash Soni
2
不要关注这个答案。这是无意义的。我认为这个答案应该属于另一个帖子,因为它教授如何使用文件提供程序都可以。 - greenapps
抱歉,这是为了Nougat。 - g7pro
这是用于在您想与另一个应用程序共享文件时使用的。 - ClassA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接