Получить путь к файлу из пойманного намерения DownloadManager

Так как Android 4.2, если пользователь загружает какой-либо файл в браузере, используется DownloadManager. Если пользователь нажимает на уведомление "загрузить полный", Intent всегда и всегда запускается. Перед Android 4.2 целью было использовать загруженный путь к файлу в контенте, чтобы:

intent.getData()

вернет строку, такую ​​как file:///storage/emulated/0/Download/some_file.ext. Однако, начиная с Android 4.2, диспетчер загрузки транслирует и намерен с помощью схемы content, например content://downloads/all_downloads/2334.

Как получить путь к локальному файлу для загруженного файла?

Я пробовал следующее:

public static String getRealPathFromURI(Uri contentUri, Activity activity) {
    DownloadManager downloadManager = (DownloadManager) activity.getSystemService(Activity.DOWNLOAD_SERVICE);
    String[] contentParts = contentUri.getEncodedPath().split("/");
    Cursor q = downloadManager.query(new DownloadManager.Query().setFilterById(Integer.parseInt(contentParts[contentParts.length - 1])));
    if (q == null) {
        // Download no longer exists
        return null;
    }
    q.moveToFirst();
    return q.getString(q.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
}

Но курсор никогда не возвращает никаких строк (поэтому q.getCount() == 0 и поэтому последний оператор return генерирует исключение). Кроме того, взлом, анализирующий идентификатор файла загрузки из Uri, кажется странным.

ОБНОВЛЕНИЕ: Я также пробовал:

input = getActivity().getContentResolver().openInputStream(contentUri);

, но это возвращает сообщение об ошибке

Отказ от прав: чтение com.android.providers.downloads.DownloadProvider uri content://downloads/all_downloads/2334 из pid = 30950, uid = 10064 требует android.permission.ACCESS_ALL_DOWNLOADS или grantUriPermission()

Ясно, что я не могу получить доступ к загрузкам (поскольку мое приложение не инициировало их - браузер сделал) через ContentProvider.

Ответ 1

Прохождение через средство распознавания контента - это правильная вещь. Не каждый URL-адрес контента будет файлом. Например, приложение Gallery даст вам uri, который переводит на сетевой вызов или локальный файл в зависимости от источника.

Даже если вы перейдете к реальному пути к файлу, вы, вероятно, не сможете его прочитать, из-за прав доступа к файлам, хотя вам может быть повезло на внешнем хранилище. Вы пытались добавить android.permission.ACCESS_ALL_DOWNLOADS к вашему приложению, как это предлагает исключение? Это не сработает, так как разрешение находится на уровне подписи:(

Ответ 2

Вот что сработало. Во-первых, вы не можете (и не должны хотеть) получить путь к файлу, как правильно указал botteaap. (Кредиты ему для установки меня по правильному пути.) Вместо этого вы автоматически получаете временное разрешение для доступа к содержимому файла, используя:

InputStream input = getContentResolver().openInputStream(intent.getData());

Вы можете прочитать этот InputStream, как и любой другой на Java. Кажется, нет способа получить имя файла. Если вам нужен файл, сначала напишите входной поток в (временный) файл.

SecurityException бросается, когда ваш временный доступ был отменен. Это произошло для меня в некоторых файлах, которые я пытался прочитать некорректно раньше и конкретно, когда Intent был выбран в моей Acticity onNewIntent.

Ответ 3

Я просто хочу добавить к ответу от @erickok, поскольку мне потребовалось несколько часов, чтобы понять это. Как указано в @jcesarmobile, вы гарантированно сможете получить имя и размер файла, а не полный путь.

Вы можете получить имя и размер следующим образом, а затем сохранить в временном файле:

String filename = null;
Long filesize = null;
Cursor cursor = null;
try {
    cursor = this.getContentResolver().query(intent.getData(), new String[] {
        OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null );
    if (cursor != null && cursor.moveToFirst()) {
        filename = cursor.getString(0);
        filesize = cursor.getLong(1);
    }
} finally {
    if (cursor != null)
        cursor.close();
}