Получить путь к файлу из пойманного намерения 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();
}