SAF — недопустимая ошибка URI из метода DocumentsContract.createDocument (копия FileOutputStream)

Мое приложение переносится на SAF или, по крайней мере, проводятся какие-то эксперименты.

Теперь он должен скопировать файл из частной папки приложения в авторизованную папку SAF.

Используемый метод:

 static boolean copyFileToTargetFolderWithNewName(Activity activity, String filePath,String targetFolderUri,String newName)
{
    File file = new File(filePath);

    FileInputStream fis=null;
    Uri docUri=null;
    try {
        fis=new FileInputStream(file);
    } catch (FileNotFoundException e) {
    return false;
    }

    deleteIfExisting(activity,Uri.parse(targetFolderUri),newName);


    ContentResolver resolver = activity.getContentResolver();
    boolean result=false;

    int offset=filePath.lastIndexOf(".");
    String ext="";
    if (offset!=-1) ext=filePath.substring(offset+1);
    String mimetype = android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
    try {
//error here
          docUri=DocumentsContract.createDocument(resolver,Uri.parse(targetFolderUri),mimetype,newName);
    } catch (FileNotFoundException e) {
        result=false;
    }

    try {
        ParcelFileDescriptor pfd=resolver.openFileDescriptor(docUri, "w");
        FileOutputStream fos=new FileOutputStream(pfd.getFileDescriptor());
        int b;
        while  ((b=fis.read()) != -1)
            fos.write(b);       
        fis.close();
        fos.close();
        pfd.close();
        result= true;
        } catch (FileNotFoundException e) {
        result=false;
        } catch (IOException e) {
        result=false;
        }
    return result;
}

я получил

java.lang.IllegalArgumentException: Invalid URI: content://com.android.providers.downloads.documents/tree/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Ffolder/subfolder

папка была создана с помощью этого метода:

static public DocumentFile createFolderInFolder(Activity activity,String parentFolderUriString,String folderName)
{
DocumentFile result=null;
ContentResolver contentResolver;
contentResolver = activity.getContentResolver();

Uri parentFolderUri=null;

Uri oldParentUri = Uri.parse(parentFolderUriString);
String id = DocumentsContract.getTreeDocumentId(oldParentUri );

parentFolderUri= DocumentsContract.buildChildDocumentsUriUsingTree(oldParentUri , id);

/*
    String id=DocumentsContract.getTreeDocumentId(Uri.parse(parentFolderUriString));

id=StringUtils.fromLastSlashRight(parentFolderUriString);
parentFolderUri= DocumentsContract.buildTreeDocumentUri(PROVIDER_AUTHORITY,id
        );
*/


DocumentFile parentFolder = DocumentFile.fromTreeUri(activity, parentFolderUri);
result=parentFolder.createDirectory(folderName);

/*try {
   result=DocumentsContract.createDocument(contentResolver,parentFolderUri,DocumentsContract.Document.MIME_TYPE_DIR,folderName);

} catch (FileNotFoundException e) {
    result =null;
}*/

return result;
}

как видите, предыдущая версия творения закомментирована, потому что не работала. Нужно было использовать DocumentFile, но, похоже, есть несовместимости с DocumentsContract. Я ошибаюсь?

Так SAF сломан? Или я кружусь?


person P5music    schedule 28.09.2019    source источник
comment
Этот Uri выглядит уродливым, с сочетанием URL-кодированных / и /. Я не совсем уверен, почему вы используете buildChildDocumentsUriUsingTree() в своем текущем коде. Насколько я знаю, ваш createFolderInFolder() должен быть просто: return DocumentFile.fromTreeUri(activity, Uri.parse(parentFolderUriString)).createDirectory(folderName);.   -  person CommonsWare    schedule 28.09.2019
comment
@CommonsWare URL-адрес состоит из двух подстрок, последняя дополняется кодом (поэтому используется символ косой черты), следует ли его избегать?   -  person P5music    schedule 30.09.2019
comment
Если вы имеете в виду, что вы сами добавили строку... вы не можете создавать свои собственные значения документа Uri. Их нужно получить у провайдера.   -  person CommonsWare    schedule 30.09.2019
comment
@CommonsWare Это uri, созданный при создании папки, так что это просто реконструкция строки, но она должна быть законной.   -  person P5music    schedule 30.09.2019


Ответы (1)


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

public static boolean copyFileToSafFolder(Context context, String filePath, String rootPath, String destFileName ) {

Uri uri = Uri.parse(rootPath);

String docId = DocumentsContract.getTreeDocumentId(uri);

Uri dirUri = DocumentsContract.buildDocumentUriUsingTree(uri, docId );

Uri destUri = null;

try
{
    destUri = DocumentsContract.createDocument(context.getContentResolver(), dirUri, "*/*", destFileName);
} catch (FileNotFoundException e )
{
    e.printStackTrace();

    return false;
}

InputStream is = null;
OutputStream os = null;
try {
     is = new FileInputStream(filePath);

     os = context.getContentResolver().openOutputStream( destUri, "w");

    byte[] buffer = new byte[1024];

    int length;
    while ((length = is.read(buffer)) > 0)
        os.write(buffer, 0, length);

    is.close();
    os.flush();
    os.close();

    return true;

} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e)     {
    e.printStackTrace();
}

return false;
}
person blackapps    schedule 28.09.2019
comment
Может ли приложение архивировать корневой Uri (из выбора пользователя) и рассматривать вложенное дерево как дочернее? Разве uri не является общим адресом? - person P5music; 30.09.2019
comment
Не понимаю, что вы спрашиваете. Вы пробовали мой код? Я сказал вам, когда именно мой код работает. - person blackapps; 30.09.2019
comment
Я должен изменить метод архивации uri в своем приложении, если мне нужно сохранить авторизованный корень, а затем иметь оставшуюся часть uri. Поэтому я и спрашиваю об обязательности. - person P5music; 30.09.2019
comment
Вы должны сохранить data.getData().toString() в onActivityResult из ACTION_OPEN_DOCUMENT_TREE. Затем используйте эту сохраненную строку в вызове функции копирования. Это все. Интересно, почему вы сохраняете другие вещи, а не data.getData().toString(). Мне также интересно, что вы имеете в виду под оставшейся частью uri. Вы можете просто протестировать функцию копирования непосредственно в onActivityResult(). - person blackapps; 30.09.2019
comment
Это работает, если я заменяю String docId = DocumentsContract.getTreeDocumentId(uri); со String docId = DocumentsContract.getDocumentId(uri); См. также stackoverflow.com/questions/58186014/ - person P5music; 02.10.2019
comment
Что работает? Мой код работает для ситуации, которую я описал. Что вы делаете непонятно. Кроме того, вы не должны были копировать мой код в новую тему. Вы должны были протестировать эту функцию здесь. И почему не заканчивается папка, существующая сначала? Оставайтесь на одном предмете, пока это не будет сделано. - person blackapps; 02.10.2019
comment
Ваш код не работает в общем случае, ведь мой вопрос касается не только сценария ACTION_OPEN_DOCUMENT_TREE. Кроме того, мой вопрос касается конкретной ошибки в конкретном сценарии. - person P5music; 02.10.2019
comment
К сожалению, вы не указали, в каком конкретном сценарии вы не опубликовали воспроизводимый код. Так что мы не знаем, что вы делаете. Но и тогда не стоило начинать новую тему. - person blackapps; 02.10.2019
comment
Сценарий является общим сценарием Uris SAF. Темы можно начинать, не беспокойтесь. - person P5music; 02.10.2019