【问题标题】:Get real path from URI of file in sdcard marshmallow从 sdcard marshmallow 中文件的 URI 获取真实路径
【发布时间】:2017-02-08 10:41:23
【问题描述】:

我想选择 sdcard 中存在的文件不在内部存储中 并将其上传到服务器,但我无法获取其获取其大小的路径。我已经开始使用以下代码选择文件:

 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);

为了获取文件的路径,我正在使用this answer,除非用户从 sdcard(可移动)中选择任何文件,否则它的工作原理。当我调试代码并发现 type 不是 primary 所以它不会进入这个条件:

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

所以我的问题是它会是什么?即如果类型不是主要的怎么办?在这种情况下我们如何获得文件路径?我搜索了很多问题和教程,其中没有其他内容。我也尝试过 this answer 的其他部分,但它不起作用,因为 System.getenv() 为“SECONDARY_STORAGE”返回 null,为“EXTERNAL_STORAGE”。尝试时出现文件未找到异常:

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

文件的 Uri 和文档 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 我已经搜索并发布了链接。如果您有任何与我的问题相关的链接,请分享它
  • my question is what will be it's else? 。你的意思是“类型”?好吧,你可以告诉!
  • 你没有告诉你需要什么文件系统路径。
  • @greenapps 立即查看

标签: android file path uri android-6.0-marshmallow


【解决方案1】:

在 Android 设备管理器上花了一些时间后,我找到了解决方案,这里是:

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

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

EDIT1:如果是 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;
}

EDIT2用于处理content://com.adobe.scan.android.documents/document/之类的主机检查码here

【讨论】:

【解决方案2】:

更改您的上传代码。您将有的地方

FileInputStream fis = new FileInputStream(path);

改成

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

所以直接使用uri。不需要文件路径。

【讨论】:

    【解决方案3】:

    ​在您的应用程序上实现 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??
    • 不要关注这个答案。这里是废话。我认为这个答案属于另一篇关于如何使用文件提供程序的帖子。
    • 牛轧糖我的坏事
    • 这适用于您想与另一个应用程序共享文件
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    相关资源
    最近更新 更多