Правильный способ получить путь к файлу при его выборе с помощью ACTION_GET_CONTENT

У меня в приложении реализовано средство выбора файлов:

Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Title"), FILE_PICK);

Это известная проблема, что вы не можете легко получить фактическое местоположение файла таким образом, потому что Intent вернет какой-то странный Uri, который вы не сможете использовать для создания объекта File.

Я использую этот метод, чтобы получить фактический путь к файлу:

/**
 * Get a file path from a Uri. This will get the the path for Storage Access
 * Framework Documents, as well as the _data field for the MediaStore and
 * other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
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)) {
            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];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            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)) {
            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())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

Это работает для некоторых файлов, а для некоторых нет. Прямо сейчас я заметил, что это не работает, когда я выбираю файл из папки "Загрузки". Он пропускает все операторы if выше и возвращает ноль.

Что было бы правильным способом, который был бы более надежным, чтобы получить фактический выбранный путь к файлу?

1 ответ

Решение

У меня в приложении реализовано средство выбора файлов

Это не "сборщик файлов". Это сборщик контента.

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

Хм, хорошо, это зависит от того, насколько вы буквальны в этом предложении.

ACTION_GET_CONTENT это не проблема в целом. Однако то, что вы получаете, не обязательно является файлом. Тем не менее, вы все равно можете загрузить их, предполагая, что все, что вы используете для загрузки, позволяет загружать из InputStream, Если так, используйте openInputStream() на ContentResolverпроходя в Uri, чтобы получить InputStream использовать.

Если вам действительно нужны настоящие файлы, вы можете реализовать интерфейс выбора файлов прямо в своем приложении. Для этого есть библиотеки. Однако вы не сможете получить доступ ко всем файлам - в частности, вы не сможете использовать это для загрузки файлов из съемного хранилища на Android 4.4+.

Другие вопросы по тегам