从不一致的Android本机文件中获取URI文件路径选择返回的URI

Asi*_*mwe 6 android file android-contentprovider android-7.0-nougat

从Android N及以上我面临这个问题.当我从左侧文件选择器抽屉上的任何应用程序中选择一个文件(例如图库和文件管理器)时,会收到URI,我可以使用文件执行我需要的操作.

此外,如果我在文件选择器的抽屉中突出显示图像/视频/音频选项卡时选择了一个文件,我选择的任何文件都会成功返回它的URI. 在此输入图像描述 在我的onActivity Result中,Log.i("File URI to String", data.getDataString());输出 I/File URI to String: content://com.android.providers.media.documents/document/image%3A20399我可以使用下面的GetPath方法获取文件路径.

但是,当我只选择内部或外部存储突出显示的选择器,注意发生,Log.i("File URI to String", data.getDataString());输出 I/File URI to String: content://com.android.externalstorage.documents/document/CA8B-8BD2%3ADCIM%2F100ANDRO%2FDSC_0001.JPG 我的getPath方法无法获得路径,我认为它可能与提供商有关,看看uris之间的差异以字符串形式.

在此输入图像描述

我如何获得这些选择的文件路径似乎不受任何提供商的支持.任何帮助表示赞赏.

我的代码启动文件选择器:

   Intent intent = new Intent();
   intent.setType("*/*");
   intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
   intent.setAction(Intent.ACTION_GET_CONTENT);
   startActivityForResult(intent, DEFAULT_IMAGE_PICK);
Run Code Online (Sandbox Code Playgroud)

当我启动文件选择器意图时

2018-10-23 21:51:26.345 1572-10747/? I/ActivityManager: START u0 {act=android.intent.action.OPEN_DOCUMENT cat=[android.intent.category.OPENABLE] typ=*/* cmp=com.android.documentsui/.picker.PickActivity (has extras)} from uid 10298
2018-10-23 21:51:26.352 1572-10747/? D/ActivityTrigger: activityStartTrigger: Activity is Triggerred in full screen ApplicationInfo{e1aa80b com.android.documentsui}
2018-10-23 21:51:26.352 1572-10747/? E/ActivityTrigger: activityStartTrigger: not whiteListedcom.android.documentsui/com.android.documentsui.picker.PickActivity/26
2018-10-23 21:51:26.353 1572-10747/? D/CompatibilityInfo: mCompatibilityFlags - 0
2018-10-23 21:51:26.353 1572-10747/? D/CompatibilityInfo: applicationDensity - 320
2018-10-23 21:51:26.353 1572-10747/? D/CompatibilityInfo: applicationScale - 1.0
2018-10-23 21:51:26.353 1572-10747/? D/ActivityTrigger: activityResumeTrigger: The activity in ApplicationInfo{e1aa80b com.android.documentsui} is now in focus and seems to be in full-screen mode
2018-10-23 21:51:26.353 1572-10747/? E/ActivityTrigger: activityResumeTrigger: not whiteListedcom.android.documentsui/com.android.documentsui.picker.PickActivity/26
2018-10-23 21:51:26.353 1572-10747/? D/ActivityTrigger: ActivityTrigger activityPauseTrigger 
2018-10-23 21:51:26.376 1572-10747/? D/ActivityTrigger: activityResumeTrigger: The activity in ApplicationInfo{e1aa80b com.android.documentsui} is now in focus and seems to be in full-screen mode
2018-10-23 21:51:26.376 1572-10747/? E/ActivityTrigger: activityResumeTrigger: not whiteListedcom.android.documentsui/com.android.documentsui.picker.PickActivity/26
2018-10-23 21:51:26.380 1572-10747/? D/CompatibilityInfo: mCompatibilityFlags - 0
2018-10-23 21:51:26.380 1572-10747/? D/CompatibilityInfo: applicationDensity - 320
2018-10-23 21:51:26.380 1572-10747/? D/CompatibilityInfo: applicationScale - 1.0
2018-10-23 21:51:26.409 1572-2271/? I/InputDispatcher: Focus entered window: Window{9ac7c5c u0 com.android.documentsui/com.android.documentsui.picker.PickActivity}
2018-10-23 21:51:26.459 1572-1634/? I/ActivityManager: Displayed com.android.documentsui/.picker.PickActivity: +80ms
Run Code Online (Sandbox Code Playgroud)

这是我的清单文件.我已经与其他应用程序实现了提供程序共享文件Uris:

<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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

在我的onActivityResult中

if ((requestCode == SELECT_GALLERY_FILE || requestCode == DEFAULT_IMAGE_PICK)
                && resultCode == Activity.RESULT_OK)
        {
           Log.i("File URI to String", data.getDataString());

            if(data.getData()!=null){//Receiving one file
                filelist.add(getPath(getActivity(),data.getData()));
                filelist = ListDeduplicator(filelist);
                Log.i("File Added", getPath(getActivity(),data.getData()));
                //Add Path of selected file to list of files to be loaded
            }

            if (data.getClipData() != null) {//Receiving several
                    ClipData mClipData = data.getClipData();
                    for (int i = 0; i < mClipData.getItemCount(); i++) {
                        //Add Paths of selected files to list of files to be loaded
                        ClipData.Item item = mClipData.getItemAt(i);
                        filelist.add(getPath(getActivity(),item.getUri()));
                        Log.i("File Added", getPath(getActivity(),item.getUri()));
                    }
                filelist = ListDeduplicator(filelist);

                }
            CheckAndExecute();
        }
Run Code Online (Sandbox Code Playgroud)

我的GetPath方法在onActivityResult中实现

public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {

            //Log.i("CHECK", "1");


            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            //Log.i("CHECK", split[1]);

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }else{
                return Search_Dir(new File("/storage") ,split[1]);
                /*for (String string : getStorageDirectories(context)) {
                    String TestPath = string+"/"+split[1];
                    if ((new File(TestPath)).exists()) {
                        return TestPath;
                    }
                }*/

            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {
            //Log.i("CHECK", "2");

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

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            //Log.i("CHECK", "3");
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[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[] {
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {
        //Log.i("CHECK", "4");
        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        //Log.i("CHECK", "5");
        return uri.getPath();
    }

    return null;

}
Run Code Online (Sandbox Code Playgroud)

Nai*_*nal 7

您无法从android的Intent.ACTION_GET_CONTENT获取通用文件URI 。URI可能与文件提供程序的不同实现方式有所不同。如果要使用公共URI,则可以实现自己的文件选择器,也可以使用某些库。

如果您的要求是仅将文件发送到服务器,则可以直接使用URI中的输入流并将此输入流发送到服务器。

有关更多信息,请参见SO 问题

在我的情况下,我处理该场景的另一种方法是从您提到的GetPath方法获取路径,如果无法从URI提供路径,则使用URI提供的输入流将文件保存在存储中的某个位置。以下是保存文件的代码:-

private void saveFileInStorage(final Uri uri) {

    new Thread(new Runnable() {
        @Override
        public void run() {
            File file = null;
            try {
                String mimeType = getActivity().getContentResolver().getType(uri);
                if (mimeType != null) {
                    InputStream inputStream = getActivity().getContentResolver().openInputStream(uri);

                        String fileName = getFileName(uri);
                        if (!fileName.equals("")) {
                            file = new File(
                                    getContext().getExternalFilesDir(
                                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + "/" + fileName);
                            OutputStream output = new FileOutputStream(file);
                            try {
                                byte[] buffer = new byte[inputStream.available()]; // or other buffer size
                                int read;

                                while ((read = inputStream.read(buffer)) != -1) {
                                    output.write(buffer, 0, read);
                                }

                                output.flush();
                                String path=file.getAbsolutePath();//use this path

                            } finally {
                                output.close();
                            }
                        }
                } 
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();

}

public String getFileName(Uri uri) {
    // The query, since it only applies to a single document, will only return
    // one row. There's no need to filter, sort, or select fields, since we want
    // all fields for one document.
    String displayName = "";
    Cursor cursor = null;
    if (getActivity() != null)
        cursor = getActivity().getContentResolver()
                .query(uri, null, null, null, null, null);
    try {
        // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (cursor != null && cursor.moveToFirst()) {

            // Note it's called "Display Name".  This is
            // provider-specific, and might not necessarily be the file name.
            displayName = cursor.getString(
                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.i(TAG, "Display Name: " + displayName);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    return displayName;
}
Run Code Online (Sandbox Code Playgroud)