В Android, начиная с API level 19 (KitKat), для доступа к файлам и каталогам, расположенным вне стандартных путей приложения, часто используется Uri (Uniform Resource Identifier) с использованием ACTION_OPEN_DOCUMENT_TREE. treeUri представляет собой Uri для дерева каталогов. Задача состоит в том, чтобы получить имя файла из этого Uri, чтобы затем загрузить содержимое файла в TBGRABitmap.
Проблема:
Получение имени файла напрямую из treeUri может быть затруднительным, так как Uri содержит информацию о месте расположения ресурса, но не обязательно само имя файла.
Решение, предложенное в исходном коде:
В предоставленном коде biologic столкнулся с этой проблемой и нашел решение. К сожалению, само решение не представлено в явном виде в первом сообщении. Второе сообщение указывает на то, что решение было найдено, но код самого решения отсутствует. Однако, исходя из контекста, можно предположить, что решение связано с использованием ContentResolver и запросом метаданных файла по Uri.
Альтернативное решение и его реализация:
Предлагается следующее решение, основанное на использовании ContentResolver для получения информации о файле, связанном с treeUri.
uses
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.Provider,
Androidapi.JNI.Net,
Androidapi.JNIBridge,
Androidapi.Helpers,
FMX.Helpers;
function GetFileNameFromTreeUri(context: JContext; treeUri: Jnet_Uri): string;
var
contentResolver: JContentResolver;
cursor: JCursor;
displayName: string;
columnIndex: integer;
uriString: string;
begin
Result := '';
if (context = nil) or (treeUri = nil) then Exit;
contentResolver := context.getContentResolver;
if contentResolver = nil then Exit;
uriString := JStringToString(treeUri.toString);
try
cursor := contentResolver.query(treeUri, nil, nil, nil, nil);
if cursor <> nil then
begin
try
// Получаем индекс столбца DISPLAY_NAME
columnIndex := cursor.getColumnIndex(TJMediaStore_MediaColumns.JavaClass.DISPLAY_NAME);
if (columnIndex > -1) and cursor.moveToFirst then
begin
// Получаем имя файла
displayName := JStringToString(cursor.getString(columnIndex));
Result := displayName;
end;
finally
cursor.close;
end;
end;
except
on E: Exception do
begin
// Обработка ошибок, например, когда запрошенный столбец не существует
System.SysUtils.ShowMessage('Error getting filename: ' + E.Message);
end;
end;
end;
Пояснения к коду:
GetFileNameFromTreeUri(context: JContext; treeUri: Jnet_Uri): string: Функция принимает JContext (контекст Android) и Jnet_Uri (объект Uri) в качестве входных параметров и возвращает имя файла в виде строки.
contentResolver := context.getContentResolver;: Получаем объект ContentResolver из контекста. ContentResolver позволяет взаимодействовать с контент-провайдерами, предоставляющими доступ к данным, таким как файлы, мультимедиа и контакты.
cursor := contentResolver.query(treeUri, nil, nil, nil, nil);: Выполняем запрос к контент-провайдеру, используя treeUri. query возвращает JCursor, который позволяет перебирать результаты запроса. nil в качестве аргументов означает, что мы запрашиваем все столбцы, не используем фильтры и сортировку.
columnIndex := cursor.getColumnIndex(TJMediaStore_MediaColumns.JavaClass.DISPLAY_NAME);: Получаем индекс столбца DISPLAY_NAME, который содержит имя файла. TJMediaStore_MediaColumns.JavaClass.DISPLAY_NAME - это константа, представляющая имя столбца.
if (columnIndex > -1) and cursor.moveToFirst then: Проверяем, что столбец DISPLAY_NAME существует и что курсор успешно переместился к первой записи (если она есть).
displayName := JStringToString(cursor.getString(columnIndex));: Получаем значение столбца DISPLAY_NAME (имя файла) из курсора и преобразуем его из JString в string Delphi.
cursor.close;: Обязательно закрываем курсор после использования, чтобы освободить ресурсы.
Обработка ошибок: Код включает блок try...except, который позволяет обрабатывать исключения, которые могут возникнуть при выполнении запроса. Это важно, поскольку контент-провайдер может быть недоступен или может не поддерживать запрошенный столбец.
Пример использования:
uses
...,
Androidapi.Helpers;
procedure TAndroidModule1.AndroidModule1ActivityResult(Sender: TObject;
requestCode: integer; resultCode: integer; intentData: JObject);
var
treeUri: Jnet_Uri;
fileName: string;
bmp: TBGRABitmap;
begin
if resultCode = RESULT_OK then
begin
if intentData <> nil then
begin
treeUri := TJIntent.Wrap(intentData).getData;
if treeUri <> nil then
begin
fileName := GetFileNameFromTreeUri(SharedActivity, treeUri);
ShowMessage('Имя файла: ' + fileName);
// Загрузка BGRABitmap
bmp := TBGRABitmap.Create;
try
bmp.LoadFromFile(JStringToString(treeUri.getPath)); // Попытка загрузки по пути из Uri
// Если не удалось, можно попробовать другой способ загрузки, например, через Stream
// bmp.LoadFromStream(...);
except
on E: Exception do
ShowMessage('Ошибка загрузки изображения: ' + E.Message);
end;
// Дальнейшая работа с bmp
// ...
bmp.Free;
end;
end;
end;
end;
Важные замечания:
Разрешения: Убедитесь, что ваше приложение имеет необходимые разрешения для доступа к файлам, указанным в treeUri. В Android 11 (API level 30) и выше необходимо использовать Storage Access Framework (SAF) и получать разрешения на доступ к каталогам. Функция Self.TakePersistableUriPermission(treeUri); в исходном коде указывает на использование SAF.
Обработка ошибок: Всегда обрабатывайте возможные ошибки при работе с ContentResolver и файлами. Например, файл может не существовать, или у приложения может не быть прав доступа к нему.
Путь к файлу:treeUri.getPath может не всегда возвращать корректный путь к файлу, особенно если файл находится в облачном хранилище или другом месте, отличном от локальной файловой системы. В таких случаях может потребоваться использование ContentResolver для получения InputStream и загрузки данных из потока.
Загрузка BGRABitmap:bmp.LoadFromFile(JStringToString(treeUri.getPath)) - это попытка загрузить изображение напрямую из файла, используя путь, полученный из Uri. Если это не работает (например, если getPath возвращает некорректный путь), необходимо использовать ContentResolver для получения InputStream и загрузки изображения из потока.
Альтернативный способ загрузки BGRABitmap через InputStream:
uses
...,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.Content,
System.IOUtils;
function LoadBGRABitmapFromUri(context: JContext; uri: Jnet_Uri): TBGRABitmap;
var
contentResolver: JContentResolver;
inputStream: JInputStream;
stream: TStream;
begin
Result := nil;
if (context = nil) or (uri = nil) then Exit;
contentResolver := context.getContentResolver;
if contentResolver = nil then Exit;
try
inputStream := contentResolver.openInputStream(uri);
if inputStream <> nil then
begin
try
stream := TJavaInputStream.Create(inputStream, false);
Result := TBGRABitmap.Create;
Result.LoadFromStream(stream);
finally
stream.Free;
inputStream.close;
end;
end;
except
on E: Exception do
begin
System.SysUtils.ShowMessage('Error loading bitmap from URI: ' + E.Message);
end;
end;
end;
В этом варианте мы получаем InputStream из Uri с помощью contentResolver.openInputStream(uri), затем создаем TStream из InputStream и загружаем TBGRABitmap из этого потока. Это более надежный способ загрузки изображений из Uri, особенно если файл находится не в локальной файловой системе.
Заключение:
Получение имени файла из treeUri требует использования ContentResolver и запроса метаданных файла. Предложенное решение предоставляет пример кода, который позволяет получить имя файла и загрузить TBGRABitmap из Uri. Важно помнить о необходимости обработки ошибок и получения необходимых разрешений для доступа к файлам. Приведены два способа загрузки BGRABitmap: напрямую из файла (если getPath возвращает корректный путь) и через InputStream (более надежный способ). Выбор способа зависит от конкретной ситуации и типа файла.
Статья описывает проблему получения имени файла из объекта treeUri в Android для загрузки BGRABitmap, предлагая решения с использованием ContentResolver и InputStream.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.