Структура доступа к хранилищу Получение правильного пути Uri Удалить / изменить / получить файл

TL: DR; Я объяснил, как использовать создание папок и подпапок с помощью DocumentFile и как удалить файл, созданный с помощью этого класса. Uri, возвращаемый onActvityResult () и documentFile.getUri.toString (), не совпадают. Мой вопрос в том, как получить действительный Uri для управления папками и файлами без использования пользовательского интерфейса SAF, если возможно, без использования взлома.

Позвольте мне поделиться тем, что я узнал до сих пор, и задать свои вопросы. Если вы хотите получить Uri папки и работать с ним, вы должны использовать Intent с ACTION_OPEN_DOCUMENT_TREE, чтобы получить Uri для доступа к папкам и установить разрешение W / R для этого uri.

Постоянное разрешение предоставлено onActivityResult с помощью:

final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(treeUri, takeFlags);

Если вы выберете основную папку устройства:

Uri treeUri = data.getData();
treeUri.toString()

Возвращает: content: //com.android.externalstorage.documents/tree/primary:

File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), "");

Возвращает: хранилище / эмуляция / 0

new File(treeUri.toString()).getAbsolutePath();

Возвращает: content: /com.android.externalstorage.documents/tree/primary:.

Если вы используете класс DocumentFile для получения пути к основной папке, вы получите

DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
String uriString = saveDir.getUri().toString();

Возвращает: file: /// storage / emulated / 0.

Мой первый вопрос: как получить Uri с контентом с помощью класса DocumentFile.

Я создаю приложение для фотографий и по умолчанию хочу установить начальную папку для изображений с помощью класса DocumentFile.

 @TargetApi(19)
protected DocumentFile getSaveDirMainMemory() {
    DocumentFile saveDir = null;
    saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
    // saveDir =
    // DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
    // saveDir =
    // DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));

    DocumentFile newDir = null;
    /*
     * Check or create Main Folder
     */

    // Check if main folder exist
    newDir = saveDir.findFile(DIR_MAIN);

    // Folder does not exist, create it
    if (newDir == null || !newDir.exists()) {
        newDir = saveDir.createDirectory(DIR_MAIN);
    }
    /*
     * Check or create Sub-Folder
     */
    DocumentFile newSubDir = null;

    // Check if sub-folder exist
    newSubDir = newDir.findFile(DIR_SUB);


    // Folder does not exist, create it
    if (newSubDir == null || !newSubDir.exists()) {
        newSubDir = newDir.createDirectory(DIR_SUB);
    }

    if (newSubDir != null && newSubDir.exists()) {
        return newSubDir;
    } else if (newDir != null && newDir.exists()) {
        return newDir;
    } else {
        return saveDir;
    }
}

Этот метод создает DIR_MAIN / DIR_SUB внутри основной памяти устройства или папку PICTURES или DCIM в зависимости от выбора. Используя эту папку по умолчанию, я сохраняю изображения в эту созданную подпапку. Я получаю newSubDir.getUri (). ToString (): file: /// storage / emulated / 0 / MainFolder / SubFolder Я назвал DIR_MAIN MainFolder, DIR_SUB: SubFolder для тестирования.

Чтобы получить доступ к изображениям или удалить их, я использую этот путь и имя изображения, которое я создал как

DocumentFile imageToDeletePath = DocumentFile.fromFile(new File(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);

imageDelete возвращает null, потому что Uri имеет неправильный формат.

Если я открою SAF ui и получу UI onActivityResult и сохраню его как строку, я использую этот метод для получения каталога и проверки разрешений Uri

@TargetApi(19)
protected DocumentFile getSaveDirNew(String uriString) {
    DocumentFile saveDir = null;

    boolean canWrite = isUriWritePermission(uriString);

    if (canWrite) {
        try {
            saveDir = DocumentFile.fromTreeUri(MainActivity.this, Uri.parse(uriString));
        } catch (Exception e) {
            saveDir = null;
        }
    }

    return saveDir;
}

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

private boolean isUriWritePermission(String uriString) {
    boolean canWrite = false;

    List<UriPermission> perms = getContentResolver().getPersistedUriPermissions();
    for (UriPermission p : perms) {
        if (p.getUri().toString().equals(uriString) && p.isWritePermission()) {
            Toast.makeText(this, "canWrite() can write URI::  " + p.getUri().toString(), Toast.LENGTH_LONG).show();
            canWrite = true;
            break;
        }
    }
    return canWrite;
}

После сохранения изображения с действующим uri и использования

DocumentFile imageToDeletePath = DocumentFile.fromTreeUri(this, Uri.parse(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);

person Thracian    schedule 20.09.2017    source источник


Ответы (1)


Uri.fromFile() и DocumentFile.fromTreeUri() создают uris из двух разных миров.

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

Нет никакого «нехакерского» способа конвертировать из одного в другое. Если вам нужно грязное решение, вы можете пойти на рефлексию (просмотрите исходный код DocumentFile.fromTreeUri и, возможно, используйте класс Storage в более новых версиях Android.

См. Также: Android - Storage Access Framework - Uri into local файл

person darken    schedule 16.09.2019