Получить путь к файлу URI из несогласованного выбора файлов для Android, возвращенного URI - PullRequest
0 голосов
/ 23 октября 2018

Начиная с Android N и выше, я сталкиваюсь с этой проблемой.Когда я выбираю файл из любого приложения в панели выбора файлов слева, например «Галерея» и «Диспетчер файлов», получаются URI, и я могу делать с файлами все, что мне нужно.

Кроме того, если я выберу файл, когда вкладка «Изображения / Видео / Аудио» выделена в ящике средства выбора файлов, для любого выбранного файла будет успешно возвращен URI.enter image description here В моем результате onActivity 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 в виде строки.

enter image description here

Как я могу получить путь к файлам этих выборов, которые, по-видимому, не поддерживаются никаким провайдером.Любая помощь приветствуется.

Мой код для запуска средства выбора файлов:

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

Когда я запускаю средство выбора файлов

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

Это мой файл манифеста. Я реализовал файл 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>

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>

В моем 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();
        }

Мой метод 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;

}

Ответы [ 5 ]

0 голосов
/ 06 ноября 2018

Вы можете использовать этот класс.

Чтобы получить файл из URI: File file = FileUtils.getFile(this, fileUri);

Надеюсь, это поможет.

0 голосов
/ 03 ноября 2018

Вы не можете получить общий URI файла из Android Intent.ACTION_GET_CONTENT .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;
}
0 голосов
/ 31 октября 2018

Если я правильно понимаю, вы пытаетесь прочитать данные, предоставленные другими приложениями, поэтому конфигурация провайдера файлов не имеет значения.

Я думаю, что проблема в вашем коде заключается в том, что вы пытаетесь извлечь файлпуть от полученного URI.Это не работает надежно.Нет гарантии, что идентификатор документа содержит путь или что вы можете запросить столбец _DATA, содержащий путь.По сути, URI контента должен быть непрозрачным дескриптором, который может использоваться только через ContentResolver методы, такие как:

У Марка Мерфи есть более подробное объяснение

0 голосов
/ 02 ноября 2018

Не могли бы вы попробовать этот код в вашем Application.onCreate() и сообщить мне, работает ли он у вас?

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
0 голосов
/ 30 октября 2018

Вы проверили this ?

FileProvider может генерировать URI контента только для файлов в каталогах, которые вы указали заранее.Чтобы указать каталог, укажите его область хранения и путь в XML, используя дочерние элементы элемента.

Прочитайте приведенный ниже путь, он поможет вам:

<files-path name="name" path="path" />
<cache-path name="name" path="path" />
<external-path name="name" path="path" />
<external-files-path name="name" path="path" />
<external-cache-path name="name" path="path" />
...