Галерея Android на Android 4.4 (KitKat) возвращает другой URI для Intent.ACTION_GET_CONTENT

214

До KitKat (или до новой галереи) Intent.ACTION_GET_CONTENTвозвращаемый URI, как этот

Содержание: // СМИ / внешние / изображения / СМИ / 3951.

Использование ContentResolverи запрашивая MediaStore.Images.Media.DATAвернул URL файла.

Однако в KitKat Галерея возвращает URI (через «Last») следующим образом:

Содержание: //com.android.providers.media.documents/document/image: 3951

Как мне справиться с этим?

Майкл Грайфенедер
источник
21
Не обращая внимания, я бы нашел способы использования контента, не требующего прямого доступа к файлу. Например, это Uriдолжно быть открыто как поток через ContentResolver. Я давно нервничаю по поводу приложений, которые предполагают, что a content:// Uri, представляющий файл, всегда может быть преобразован в a File.
CommonsWare
1
@ CommonsWare, если я хочу сохранить путь к изображению в sqlite db, чтобы я мог открыть его позже, я должен сохранить URI или абсолютный путь к файлу?
Снейк
2
@CommonsWare Я согласен с вашей нервозностью. :-) Однако мне нужно иметь возможность передать имя файла (для изображения) в нативный код. Решение состоит в том, чтобы скопировать данные, полученные с помощью, InputStreamв ContentResolverзаранее определенное место, чтобы у него было известное имя файла. Тем не менее, это звучит расточительно для меня. Любые другие предложения?
Дарренп
2
@darrenp: Ммм ... переписать нативный код для работы с более InputStreamчем JNI? К сожалению, вариантов для вас не так много.
CommonsWare
1
Это полезно знать. Спасибо за ваш ответ. С тех пор я узнал, что теперь мы передаем изображение в C ++ в памяти, а не через файл, так что теперь мы можем использовать InputStreamвместо файла (что здорово). Только чтение тега EXIF ​​немного сложно и требует библиотеки Drew Noakes . Большое спасибо за ваши комментарии.
Дарренп

Ответы:

108

Попробуй это:

if (Build.VERSION.SDK_INT <19){
    Intent intent = new Intent(); 
    intent.setType("image/jpeg");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED);
} else {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/jpeg");
    startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) return;
    if (null == data) return;
    Uri originalUri = null;
    if (requestCode == GALLERY_INTENT_CALLED) {
        originalUri = data.getData();
    } else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) {
        originalUri = data.getData();
        final int takeFlags = data.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // Check for the freshest data.
        getContentResolver().takePersistableUriPermission(originalUri, takeFlags);
    }

    loadSomeStreamAsynkTask(originalUri);

}

Наверное нужно

@SuppressLint ( "NewApi")

для

takePersistableUriPermission

искатель
источник
1
Не могли бы вы уточнить, что делает код KitKat? Требуется ли для этого новое разрешение? Предварительный код KitKat у меня работает и на KitKat. Так почему бы мне выбрать другой код для KitKat? Спасибо.
Майкл Грайфенедер
67
кажется, мы не можем получить путь от новых SDK URI. Также обидно, что Google сделал такие изменения без надлежащей документации и объявления.
user65721
1
Не могли бы вы объяснить, как получить URL файла? Я хотел бы получить реальный путь в SDCard. Например, если это изображение, я хотел бы получить этот путь /storage/sdcard0/DCIM/Camera/IMG_20131118_153817_119.jpg вместо документа Uri.
Альваро
4
Основываясь на документации KitKat ( developer.android.com/about/versions/… ), это может быть не то, что нужно ОП, если только он не намерен использовать / редактировать документы, принадлежащие другим приложениям. Если ОП хочет получить копию или сделать что-то в соответствии со старыми версиями, ответ @voytez будет более уместным.
Колин М.
8
Это не работает для меня. Я получаю следующее исключение (на складе 4.4.2): E / AndroidRuntime (29204): вызвано: java.lang.SecurityException: запрошенные флаги 0x1, но разрешены только 0x0
Рассел Стюарт
177

Это не требует специальных разрешений и работает с Storage Access Framework, а также с неофициальным ContentProviderшаблоном (путь к файлу в _dataполе).

/**
 * 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;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @param selection (Optional) Filter used in the query.
 * @param selectionArgs (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri, String selection,
        String[] selectionArgs) {

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

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}


/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}

Смотрите актуальную версию этого метода здесь .

Пол Берк
источник
2
Это здорово работает на 4.4 Nexus 5 Documents UI и некоторых других устройствах KitKat, использующих стандартные приложения галереи, спасибо Пол!
Джош
1
Спасибо за это, мне потребовались целые годы, чтобы продвинуться так далеко с SDK 19 !! Моя проблема в том, что мое устройство использует Google диск в качестве файлового браузера. Если файл находится на устройстве, путь к изображению исправен, но если файл находится на диске, он не открывается. Может быть, мне просто нужно посмотреть на открытие изображений с Google Drive. Дело в том, что мое приложение написано, чтобы использовать путь к файлу и получать изображения с использованием выборки ...
RuAware
2
@RuAware Когда вы выбираете файл диска, он возвращает Authority: com.google.android.apps.docs.storageи Segments: [document, acc=1;doc=667]. Я не уверен, но предположим, что docзначение - это Uriидентификатор, к которому вы можете обратиться. Скорее всего, вам потребуется настроить разрешения, как описано в разделе «Авторизация приложения на Android» здесь: developers.google.com/drive/integrate-android-ui . Пожалуйста, обновите здесь, если вы выясните это.
Пол Берк
30
это абсолютно ужасно! Вы не должны продолжать распространять код, который «обманывает», как это. он поддерживает только те исходные приложения, для которых известен шаблон, и весь смысл модели провайдера документов заключается в поддержке произвольных источников
j__m
2
Не _dataбудет работать, если ContentProvider не поддерживает его. Рекомендуется следовать инструкциям @CommonsWare и больше не использовать полный путь к файлу, поскольку это может быть файл в облаке Dropbox вместо реального файла.
Сошиал
67

Была та же проблема, пробовал решение выше, но хотя оно работало в целом, по какой-то причине я получал отказ в разрешении на контент-провайдера Uri для некоторых изображений, хотя у меня было android.permission.MANAGE_DOCUMENTSправильно добавлено разрешение.

В любом случае нашел другое решение - принудительно открывать галерею изображений вместо просмотра документов KITKAT:

// KITKAT

i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, CHOOSE_IMAGE_REQUEST);

и затем загрузите изображение:

Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
                BitmapFactory.decodeStream(input , null, opts);

РЕДАКТИРОВАТЬ

ACTION_OPEN_DOCUMENT может потребоваться сохранение флагов разрешений и т. д., что обычно приводит к исключениям безопасности ...

Другое решение состоит в том, чтобы использовать ACTION_GET_CONTENTкомбинацию, c.getContentResolver().openInputStream(selectedImageURI)которая будет работать как на pre-KK, так и на KK. Kitkat будет использовать просмотр новых документов, и это решение будет работать со всеми приложениями, такими как Фото, Галерея, Обозреватель файлов, Dropbox, Google Drive и т. Д.), Но помните, что при использовании этого решения вы должны создать изображение onActivityResult()и сохранить его на SD-карта, например. Воссоздание этого изображения из сохраненного URI при следующем запуске приложения приведет к возникновению исключения безопасности в средстве распознавания контента, даже если вы добавите флаги разрешений, как описано в документации по API Google (именно это и произошло, когда я провел некоторое тестирование)

Кроме того, в Руководстве по API для разработчиков Android предлагается следующее:

ACTION_OPEN_DOCUMENT не предназначен для замены ACTION_GET_CONTENT. То, что вы должны использовать, зависит от потребностей вашего приложения:

Используйте ACTION_GET_CONTENT, если вы хотите, чтобы ваше приложение просто считывало / импортировало данные. При таком подходе приложение импортирует копию данных, например файл изображения.

Используйте ACTION_OPEN_DOCUMENT, если вы хотите, чтобы ваше приложение имело долгосрочный постоянный доступ к документам, принадлежащим провайдеру документов. Примером может служить приложение для редактирования фотографий, которое позволяет пользователям редактировать изображения, хранящиеся в поставщике документов.

voytez
источник
1
Этот ответ содержал правильную информацию для моих целей. Условно использование ACTION_PICK и EXTERNAL_CONTENT_URI на KitKat предоставило такую ​​же возможность получать метаданные об изображениях в галерее через ContentResolver, как это возможно в более старых версиях с использованием просто ACTION_GET_CONTENT.
Колин М.
@voytez, Может ли этот URI возвращаться через ваше сообщение, преобразованное в полный реальный путь к изображению?
Снейк
Я считаю, что он должен работать так же, как и до KitKat, так как этот код заставляет открывать галерею изображений вместо просмотра документов KK. Но если вы намереваетесь использовать его для создания образа, тогда это решение лучше, поскольку переход на реальный путь - это лишний ненужный шаг.
Войтез
4
У меня тоже сработало, а не Intent.ACTION_GET_CONTENT. В любом случае я сохранил Intent.createChooser()оболочку нового Intent, чтобы пользователь мог выбрать приложение для просмотра, и работал как положено. Может кто-то видит недостатки этого решения?
Лоренцо-с
1
Для всех, кому интересно, цитата взята с developer.android.com/guide/topics/providers/…
snark
38

Как уже упоминалось в Commonsware, вы не должны предполагать, что поток, который вы получаете, ContentResolverконвертируется в файл.

Что вы действительно должны сделать, это открыть InputStreamиз ContentProvider, а затем создать растровое изображение из него. И это работает на 4.4 и более ранних версиях, не нужно размышлять.

    //cxt -> current context

    InputStream input;
    Bitmap bmp;
    try {
        input = cxt.getContentResolver().openInputStream(fileUri);
        bmp = BitmapFactory.decodeStream(input);
    } catch (FileNotFoundException e1) {

    }

Конечно, если вы обрабатываете большие изображения, вы должны загрузить их с соответствующими inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Но это другая тема.

Михал К
источник
Это не работает для меня с Nexus 4 под управлением Kitkat, но это с Galaxy S3 под управлением 4.1.2
Dan2552
@ Dan2552 какая часть не работает? Есть ли у вас какие-либо исключения?
Михал К
Оказалось, я использовал неправильный умышленный вызов в галерею. Я использовал тот, который был для любого вида документов, но с фильтром расширения файла.
Dan2552
2
Какой красиво простой ответ, спасибо! Для других, следующих за этим ответом, «cxt» относится к текущему контексту и обычно будет «this».
Томасфорт
1
Это, вероятно, означает, что файла просто нет. URI, кажется, в порядке.
Михал К
33

Я считаю, что уже опубликованные ответы должны направить людей в правильном направлении. Однако вот что я сделал, это имело смысл для устаревшего кода, который я обновлял. Унаследованный код использовал URI из галереи, чтобы изменить, а затем сохранить изображения.

До версии 4.4 (и google drive) URI выглядели так: content: // media / external / images / media / 41

Как указано в вопросе, они чаще всего выглядят так: content: //com.android.providers.media.documents/document/image: 3951

Поскольку мне нужна была возможность сохранять изображения, а не нарушать уже существующий код, я просто скопировал URI из галереи в папку данных приложения. Затем был создан новый URI из файла сохраненного изображения в папке данных.

Вот идея:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent), CHOOSE_IMAGE_REQUEST);

public void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image");

    //Copy URI contents into temporary file.
    try {
        tempFile.createNewFile();
        copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile));
    }
    catch (IOException e) {
        //Log Error
    }

    //Now fetch the new URI
    Uri newUri = Uri.fromFile(tempFile);

    /* Use new URI object just like you used to */
 }

Примечание. CopyAndClose () просто выполняет файловый ввод-вывод для копирования InputStream в FileOutputStream. Код не опубликован.

ЛЕО
источник
довольно умно я тоже нуждался в фактическом файле uri
moreKing
ты мой герой, это именно то, что мне нужно! отлично работает и с файлами на Google Диске
Мишкин
Думай из коробки, верно? : D Этот код работает точно так, как я ожидал.
WeirdElfB0y
2
опубликовать код для копирования и закрыть, ответ не полный
Bartek Pacia
24

Просто хотел сказать, что этот ответ блестящий, и я давно его использую без проблем. Но некоторое время назад я натолкнулся на проблему, заключающуюся в том, что DownloadsProvider возвращает URI в формате content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdfи, следовательно, происходит сбой приложения, NumberFormatExceptionпоскольку невозможно анализировать его сегменты URI так долго. Но raw:сегмент содержит прямой URI, который может быть использован для получения ссылочного файла. Итак, я исправил это, заменив isDownloadsDocument(uri) ifсодержимое следующим:

final String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
    return id.replaceFirst("raw:", "");
}
try {
    final Uri contentUri = ContentUris.withAppendedId(
            Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
    Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e);
    return null;
}
}
Bringoff
источник
2
Работает отлично! Спасибо
mikemike396
8

Я объединил несколько ответов в одно рабочее решение, которое приводит к пути к файлу

Тип MIME не имеет значения для примера.

            Intent intent;
            if(Build.VERSION.SDK_INT >= 19){
                intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
                intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
            }else{
                intent = new Intent(Intent.ACTION_GET_CONTENT);
            }
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setType("application/octet-stream");
            if(isAdded()){
                startActivityForResult(intent, RESULT_CODE);
            }

Результат обработки

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) {
        Uri uri = data.getData();
        if (uri != null && !uri.toString().isEmpty()) {
            if(Build.VERSION.SDK_INT >= 19){
                final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
                //noinspection ResourceType
                getActivity().getContentResolver()
                        .takePersistableUriPermission(uri, takeFlags);
            }
            String filePath = FilePickUtils.getSmartFilePath(getActivity(), uri);
            // do what you need with it...
        }
    }
}

FilePickUtils

import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class FilePickUtils {
    private static String getPathDeprecated(Context ctx, Uri uri) {
        if( uri == null ) {
            return null;
        }
        String[] projection = { MediaStore.Images.Media.DATA };
        Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null);
        if( cursor != null ){
            int column_index = cursor
                    .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        }
        return uri.getPath();
    }

    public static String getSmartFilePath(Context ctx, Uri uri){

        if (Build.VERSION.SDK_INT < 19) {
            return getPathDeprecated(ctx, uri);
        }
        return  FilePickUtils.getPath(ctx, uri);
    }

    @SuppressLint("NewApi")
    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 getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

}
Гжегож Павельчук
источник
я столкнулся с проблемой .... uri.getPath () возвращал URI с / external, и он не возвращал путь. Я добавил это еще, если ("content" .equalsIgnoreCase (uri.getScheme ())) блок, и это работает хорошо сейчас.
Можете ли
Filepath получает нуль .. Ури: содержание: //com.android.externalstorage.documents/document/799B-1419%3AScreenshot%2FScreenshot_20181117_162826.png
Bhavesh Moradiya
7

Вопрос

Как получить фактический путь к файлу из URI

Ответ

Насколько мне известно, нам не нужно получать путь к файлу из URI, потому что в большинстве случаев мы можем напрямую использовать URI для выполнения нашей работы (например, 1. получение растрового изображения 2. отправка файла на сервер и т. Д. .)

1. Отправка на сервер

Мы можем напрямую отправить файл на сервер, используя только URI.

Используя URI, мы можем получить InputStream, который мы можем напрямую отправить на сервер, используя MultiPartEntity.

пример

/**
 * Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application).
 *
 * @param uri     URI.
 * @param context Context.
 * @return Multi Part Entity.
 */
public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) {
    MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
    try {
        InputStream inputStream = mContext.getContentResolver().openInputStream(uri);
        if (inputStream != null) {
            ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context));
            multipartEntity.addPart("[YOUR_KEY]", contentBody);
        }
    }
    catch (Exception exp) {
        Log.e("TAG", exp.getMessage());
    }
    return multipartEntity;
}

/**
 * Used to get a file name from a URI.
 *
 * @param uri     URI.
 * @param context Context.
 * @return File name from URI.
 */
public String getFileNameFromUri(final Uri uri, final Context context) {

    String fileName = null;
    if (uri != null) {
        // Get file name.
        // File Scheme.
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
            File file = new File(uri.getPath());
            fileName = file.getName();
        }
        // Content Scheme.
        else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
            Cursor returnCursor =
                    context.getContentResolver().query(uri, null, null, null, null);
            if (returnCursor != null && returnCursor.moveToFirst()) {
                int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                fileName = returnCursor.getString(nameIndex);
                returnCursor.close();
            }
        }
    }
    return fileName;
}

2. Получение BitMap из URI

Если URI указывает на изображение, мы получим растровое изображение, иначе null:

/**
 * Used to create bitmap for the given URI.
 * <p>
 * 1. Convert the given URI to bitmap.
 * 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap.
 * 3. Create bitmap bitmap depending on the ration from URI.
 * 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri
 *
 * @param context       Context.
 * @param uri           URI to the file.
 * @param bitmapSize Bitmap size required in PX.
 * @return Bitmap bitmap created for the given URI.
 * @throws IOException
 */
public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException {

    // 1. Convert the given URI to bitmap.
    InputStream input = context.getContentResolver().openInputStream(uri);
    BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
    onlyBoundsOptions.inJustDecodeBounds = true;
    onlyBoundsOptions.inDither = true;//optional
    onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
    input.close();
    if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
        return null;
    }

    // 2. Calculate ratio.
    int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
    double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0;

    // 3. Create bitmap.
    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
    bitmapOptions.inDither = true;//optional
    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    input = context.getContentResolver().openInputStream(uri);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
    input.close();

    return bitmap;
}

/**
 * For Bitmap option inSampleSize - We need to give value in power of two.
 *
 * @param ratio Ratio to be rounded of to power of two.
 * @return Ratio rounded of to nearest power of two.
 */
private static int getPowerOfTwoForSampleRatio(final double ratio) {
    int k = Integer.highestOneBit((int) Math.floor(ratio));
    if (k == 0) return 1;
    else return k;
}

Комментарии

  1. Android не предоставляет никаких методов для получения пути к файлу по URI, и в большинстве приведенных выше ответов мы жестко закодировали некоторые константы, которые могут нарушаться при выпуске функции (извините, я могу ошибаться).
  2. Прежде чем перейти непосредственно к решению получения пути к файлу из URI, попробуйте, если вы можете решить ваш сценарий использования с URI и методами Android по умолчанию.

Ссылка

  1. https://developer.android.com/guide/topics/providers/content-provider-basics.html
  2. https://developer.android.com/reference/android/content/ContentResolver.html
  3. https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html
Взант
источник
Спасибо. Использование Uri и ContentResolver таким образом значительно упростило мое приложение при работе с файлами.
Jacksonakj
6

Эта библиотека Android обрабатывает изменения в KitKat (включая более старые версии - 2.1+):
https://github.com/iPaulPro/aFileChooser

Используйте String path = FileUtils.getPath(context, uri)для преобразования возвращаемого Uri в строку пути, используемую на всех версиях ОС. Подробнее об этом здесь: https://stackoverflow.com/a/20559175/860488

Мортен Холмгаард
источник
3

Для тех, кто все еще использует код @Paul Burke с Android SDK версии 23 и выше, если ваш проект встретился с ошибкой, говорящей о том, что вам не хватает EXTERNAL_PERMISSION, и вы очень уверены, что уже добавили разрешение пользователя в свой файл AndroidManifest.xml. Это потому, что вы можете использовать Android API 23 или выше, и Google заставит вас снова гарантировать разрешение, пока вы выполняете действие для доступа к файлу во время выполнения.

Это означает: если ваша версия SDK 23 или выше, вас просят о разрешении ЧИТАТЬ и ЗАПИСАТЬ, когда вы выбираете файл изображения и хотите узнать его URI.

И следующий мой код, в дополнение к решению Пола Бёрка. Я добавляю этот код, и мой проект начинает работать нормально.

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSINOS_STORAGE = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE
};

public static void verifyStoragePermissions(Activity activity) {
    int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(
                activity,
                PERMISSINOS_STORAGE,
                REQUEST_EXTERNAL_STORAGE
        );
    }
}

И в вашей деятельности и фрагменте, где вы запрашиваете URI:

private void pickPhotoFromGallery() {

    CompatUtils.verifyStoragePermissions(this);
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    // startActivityForResult(intent, REQUEST_PHOTO_LIBRARY);
    startActivityForResult(Intent.createChooser(intent, "选择照片"),
            REQUEST_PHOTO_LIBRARY);
}

В моем случае CompatUtils.java - это место, где я определяю метод verifyStoragePermissions (как статический тип, чтобы я мог вызывать его в рамках другого действия).

Кроме того, это имеет больше смысла, если вы сначала создадите условие if, чтобы увидеть, является ли текущая версия SDK выше 23 или нет, прежде чем вызывать метод verifyStoragePermissions.

Anthonyeef
источник
2

Вот что я делаю:

Uri selectedImageURI = data.getData();    imageFile = new File(getRealPathFromURI(selectedImageURI)); 

private String getRealPathFromURI(Uri contentURI) {
  Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
  if (cursor == null) { // Source is Dropbox or other similar local file path
      return contentURI.getPath();
      } else { 
      cursor.moveToFirst(); 
      int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 
      return cursor.getString(idx); 
  }
}

ПРИМЕЧАНИЕ: managedQuery()метод устарел, поэтому я не использую его.

Этот ответ от m3n0R на вопрос android получить реальный путь по Uri.getPath (), и я не претендую на кредит. Я просто думал, что люди, которые еще не решили эту проблему, могли бы использовать это.

Исаак Заис
источник
2
Это не ответ на новое приложение «Галерея» (строго «Поставщик медиа-документов») на KitKat.
nagoya0
Приложение, которое спрашивающий называет «Галерея», вероятно, является новым средством выбора файлов на kitkat. FYI: addictivetips.com/android/...
nagoya0
Я сделал подобное и получил IndexOutOfBound на Nexus 5X, Android 6 на этой строке:cursor.getString(idx);
Osify
1

Пожалуйста, постарайтесь избегать использования метода takePersistableUriPermission, потому что это вызвало для меня исключение времени выполнения. / ** * Выбрать из галереи. * /

public void selectFromGallery() {
    if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) {

        Intent intent = new Intent(); 
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        ((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);

    } else {

        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        ((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED);
    }
}

OnActivity для результата для обработки данных изображения:

@ Override protected void onActivityResult (int requestCode, int resultCode, данные намерения) {

    //gallery intent result handling before kit-kat version
    if(requestCode==AppConstants.GALLERY_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        String[] filePathColumn = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String filePath = cursor.getString(columnIndex);
        cursor.close();
        photoFile = new File(filePath);
        mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);

    }
    //gallery intent result handling after kit-kat version
    else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        InputStream input = null;
        OutputStream output = null;

        try {
            //converting the input stream into file to crop the 
            //selected image from sd-card.
            input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
            try {
                photoFile = mImgCropping.createImageFile();
            } catch (IOException e) {
                e.printStackTrace();
            }catch(Exception e) {
                e.printStackTrace();
            }
            output = new FileOutputStream(photoFile);

            int read = 0;
            byte[] bytes = new byte[1024];

            while ((read = input.read(bytes)) != -1) {
                try {
                    output.write(bytes, 0, read);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
Saranya
источник
Где вы показываете изображение во втором случае?
Quantum_VC
извините, я не смог добавить эту строку кода в else, если mImgCropping.startCropImage (photoFile, AppConstants.REQUEST_IMAGE_CROP); снова нужно вызвать функцию imagecropping в соответствии с потребностями моего проекта
saranya
Какой тип файла является photoFile и mImgCropping?
Филипп BH
1

Если кому-то интересно, я сделал рабочую версию Kotlin для ACTION_GET_CONTENT:

var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (path.contains("/document/image:")) { // files selected from "Documents"
    databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    selection = "_id=?"
    selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
    databaseUri = uri
    selection = null
    selectionArgs = null
}
try {
    val projection = arrayOf(MediaStore.Images.Media.DATA,
        MediaStore.Images.Media._ID,
        MediaStore.Images.Media.ORIENTATION,
        MediaStore.Images.Media.DATE_TAKEN) // some example data you can query
    val cursor = context.contentResolver.query(databaseUri,
        projection, selection, selectionArgs, null)
    if (cursor.moveToFirst()) {
        // do whatever you like with the data
    }
    cursor.close()
} catch (e: Exception) {
    Log.e(TAG, e.message, e)
}
0101100101
источник
Я просто хочу рабочий код kotlin. Это работа для меня. спасибо
Харви
1

Я попробовал несколько ответов здесь, и я думаю, что у меня есть решение, которое будет работать каждый раз, а также управлять разрешениями.

Он основан на умном решении от LEO. Этот пост должен содержать весь код, необходимый для работы, и должен работать на любом телефоне и версии Android;)

Чтобы иметь возможность выбрать файл с SD-карты, вам понадобится это в вашем манифесте:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Константы:

private static final int PICK_IMAGE = 456; // Whatever number you like
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like
public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like

Проверьте разрешение и запуститеImagePick, если это возможно

if (ContextCompat.checkSelfPermission(getThis(),
        Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {

    ActivityCompat.requestPermissions(getThis(),
            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL);
}
else {
    launchImagePick();
}

Ответ разрешения

@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull
                                         String permissions[],
                                       @NonNull
                                         int[] grantResults) {

    if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) {
        launchImagePick();
    }
}

Управление разрешением ответа

public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) {

    if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {

        // If request is cancelled, the result arrays are empty.

        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            // Permission was granted, yay! Do the
            // contacts-related task you need to do.
            return true;

        } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {

            boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.READ_EXTERNAL_STORAGE);

            if (!showRationale) {
                // The user also CHECKED "never ask again".
                // You can either enable some fall back,
                // disable features of your app
                // or open another dialog explaining
                // again the permission and directing to
                // the app setting.

            } else {
                // The user did NOT check "never ask again".
                // This is a good place to explain the user
                // why you need the permission and ask if he/she wants
                // to accept it (the rationale).
            }
        } else {
            // Permission denied, boo! Disable the
            // functionality that depends on this permission.
        }
    }
    return false;
}

Запуск выбора изображения

private void launchImagePick() {

    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    startActivityForResult(intent, PICK_IMAGE);

    // see onActivityResult
}

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

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE) {

        if (resultCode == Activity.RESULT_OK) {
            if (data != null && data.getData() != null) {

                try {
                     InputStream inputStream = getContentResolver().openInputStream(data.getData())
                     if (inputStream != null) {

                        // No special persmission needed to store the file like that
                        FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE);

                        final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead = -1;
                        while ((bytesRead = inputStream.read(buffer)) > -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                        inputStream.close();
                        fos.close();

                        File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME);

                        // Do whatever you want with the File

                        // Delete when not needed anymore
                        deleteFile(FILE_TEMP_NAME);
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                // Error display
            }
        } else {
            // The user did not select any image
        }
    }
}

Это все люди; это работает для меня на всех телефонах, которые у меня есть.

Квентин Г.
источник
0

Это полный взлом, но вот что я сделал ...

Поэтому, играя с настройкой DocumentsProvider , я заметил, что пример кода ( getDocIdForFileоколо строки 450) генерирует уникальный идентификатор для выбранного документа на основе (уникального) пути к файлу относительно указанного корня, который вы ему даете (то есть то, что вы установили mBaseDirв строке 96).

Таким образом, URI выглядит примерно так:

content://com.example.provider/document/root:path/to/the/file

Как говорят в документах, он предполагает только один корень (в моем случае это, Environment.getExternalStorageDirectory()но вы можете использовать где-то еще ... затем он берет путь к файлу, начиная с корня, и делает его уникальным идентификатором, предваряя " root:". Поэтому я Можно определить путь, "/document/root:удалив "part из uri.getPath (), создав реальный путь к файлу, выполнив что-то вроде этого:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check resultcodes and such, then...
uri = data.getData();
if (uri.getAuthority().equals("com.example.provider"))  {
    String path = Environment.getExternalStorageDirectory(0.toString()
                 .concat("/")
                 .concat(uri.getPath().substring("/document/root:".length())));
    doSomethingWithThePath(path); }
else {
    // another provider (maybe a cloud-based service such as GDrive)
    // created this uri.  So handle it, or don't.  You can allow specific
    // local filesystem providers, filter non-filesystem path results, etc.
}

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

(Также есть лучший способ построить путь, который не предполагает, что «/» является разделителем пути и т. Д. Но вы поняли идею.)

fattire
источник
Чтобы ответить себе еще более сумасшедшей мыслью - если ваше приложение уже обрабатывает file://намерения из внешнего средства выбора файлов, вы также можете проверить полномочия, как в примере выше, чтобы убедиться, что это от вашего собственного провайдера, и если это так, вы можете также используйте путь, чтобы «подделать» новое file://намерение, используя путь, который вы извлекли, StartActivity()и позвольте вашему приложению забрать его. Я знаю, ужасно.
толстяк
0

Это работало нормально для меня:

else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK)
{
    Uri uri = data.getData();
    Log.i(TAG, "old uri =  " + uri);
    dumpImageMetaData(uri);

    try {
        ParcelFileDescriptor parcelFileDescriptor =
                getContentResolver().openFileDescriptor(uri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Log.i(TAG, "File descriptor " + fileDescriptor.toString());

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);

        options.inSampleSize =
           BitmapHelper.calculateInSampleSize(options,
                                              User.PICTURE_MAX_WIDTH_IN_PIXELS,
                                              User.PICTURE_MAX_HEIGHT_IN_PIXELS);
        options.inJustDecodeBounds = false;

        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
        imageViewPic.setImageBitmap(bitmap);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        // get byte array here
        byte[] picData = stream.toByteArray();
        ParseFile picFile = new ParseFile(picData);
        user.setProfilePicture(picFile);
    }
    catch(FileNotFoundException exc)
    {
        Log.i(TAG, "File not found: " + exc.toString());
    }
}
Rafa
источник
забыть dumpImageMetaData (uri); это ненужно
Рафа
0

Основываясь на ответе Пола Бёрка, я столкнулся со многими проблемами при разрешении пути URI внешней SD-карты, поскольку большинство предлагаемых «встроенных» функций возвращают пути, которые не разрешаются в файлы.

Тем не менее, это мой подход к его обработке // TODO неосновных томов .

String resolvedPath = "";
File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
for (File f : possibleExtSdComposites) {
    // Reset final path
    resolvedPath = "";

    // Construct list of folders
    ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));

    // Look for folder "<your_application_id>"
    int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);

    // ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
    ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));

    // Construct list containing full possible path to the file
    hierarchyList.add(tail);
    String possibleFilePath = TextUtils.join("/", hierarchyList);

    // If file is found --> success
    if (idx != -1 && new File(possibleFilePath).exists()) {
        resolvedPath = possibleFilePath;
        break;
    }
}

if (!resolvedPath.equals("")) {
    return resolvedPath;
} else {
    return null;
}

Обратите внимание, что это зависит от иерархии, которая может отличаться для каждого производителя телефона - я не проверял их все (до сих пор это работало хорошо на Xperia Z3 API 23 и Samsung Galaxy A3 API 23).

Пожалуйста, подтвердите, если это не работает хорошо в другом месте.

Evusas
источник
-1

Ответ @paul burke отлично работает как для изображений с камеры, так и для галереи для API уровня 19 и выше, но не работает, если минимальный SDK вашего проекта Android установлен ниже 19, а некоторые ответы, приведенные выше, не работают как для галереи, так и для галереи. камера. Ну, я изменил код @paul burke, который работает для уровня API ниже 19. Ниже приведен код.

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

    final boolean isKitKat = Build.VERSION.SDK_INT >=
                             Build.VERSION_CODES.KITKAT;
    Log.i("URI",uri+"");
    String result = uri+"";

    // DocumentProvider
    // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
    if (isKitKat && (result.contains("media.documents"))) {

        String[] ary = result.split("/");
        int length = ary.length;
        String imgary = ary[length-1];
        final String[] dat = imgary.split("%3A");

        final String docId = dat[1];
        final String type = dat[0];

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

        final String selection = "_id=?";
        final String[] selectionArgs = new String[] {
            dat[1]
        };

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

    return null;
}

public static String getDataColumn(Context context, Uri uri, String selection,
                                   String[] selectionArgs) {
    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    }
    finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}
Парвез Рафи
источник
Я получаю java.lang.IllegalArgumentException: ни один из запрошенных столбцов не может быть предоставлен при выборе изображения Google Doc
dirkoneill
@dirkoneill Я получаю то же исключение. Вы нашли исправление?
Генри
-4

Ответ на ваш вопрос заключается в том, что вам нужно иметь разрешения. Введите следующий код в файле manifest.xml:

<uses-sdk  android:minSdkVersion="8"   android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission>
<uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`

У меня это сработало ...

Ashwin
источник