9

In my App the user can choose a directory where to create an Excel file using the implicit intent ACTION_OPEN_DOCUMENT_TREE. However, the Uri returned in onActivityResult() cannot be used by FileOutputStream(). It throws a FileNotFoundException:

java.io.FileNotFoundException: content:/com.android.externalstorage.documents/tree/home%3A:test.xlsx (No such file or directory)

In onActivityResult() I check if the path exists via File.exists() and if not, I want to create a new Excel file.

onActivityResult():

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        Log.d(TAG, "onActivityResult: called");
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK && requestCode == 2) {
            Log.d(TAG, "onActivityResult: path = " + data.getData()
                                                         .getPath());
            Uri treePath = data.getData();
            File path = new File(treePath + File.pathSeparator + "test.xlsx");
            if (path.exists()) {
                updateExistingExcelFile(path);
            } else {
                createNewExcelFile(path);
            }
        }
    }

createNewExcelFile():

    private void createNewExcelFile(File path) {
        Log.d(TAG, "createNewExcelFile: called");
        Workbook workbook = new HSSFWorkbook();
        Cell cell;
        Sheet sheet;
        sheet = workbook.createSheet("Name of sheet");
        Row row = sheet.createRow(0);
        cell = row.createCell(0);
        cell.setCellValue("Name");
        cell = row.createCell(1);
        cell.setCellValue("Number");
        sheet.setColumnWidth(0, (10 * 200));
        sheet.setColumnWidth(1, (10 * 200));
        FileOutputStream fileOutputStream;
        try {
            fileOutputStream = new FileOutputStream(path);
            workbook.write(fileOutputStream);
            Toast.makeText(this, "Created", Toast.LENGTH_LONG)
                 .show();
            fileOutputStream.close();
        } catch (IOException e) {
            Log.e(TAG, "createNewExcelFile: ", e);
        }
    }

The code works perfectly fine if I use Activity.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) or something similar instead of the implicit intents path.

René Jörg Spies
  • 1,544
  • 1
  • 10
  • 29
  • 1
    Don't try and convert the URI to a real path see https://stackoverflow.com/q/26972539/2373819 – Andrew Apr 09 '20 at 10:48

1 Answers1

22

Step #1: Take the Uri that you get from ACTION_OPEN_DOCUMENT_TREE and pass it to DocumentFile.fromTreeUri().

Step #2: Call createFile() on that DocumentFile to get a DocumentFile representing the child document.

Step #3: Call getUri() on the DocumentFile that you created in Step #2.

Step #4: Call openOutputStream() on a ContentResolver, passing in the Uri from Step #3, to get an OutputStream that you can use to write content. You get a ContentResolver by calling getContentResolver() on some Context, such as an Activity.

See this blog post for more on using ACTION_OPEN_DOCUMENT_TREE.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks, the file is now created. However I am getting a ``SecurityException`` ``java.lang.SecurityException: No persistable permission grants found for UID 10207 and Uri content://com.android.externalstorage.documents/tree/home:/document/home:test.xlsx [user 0]``. In ``onActivityResult()`` I am calling ``takePersistableUriPermission()`` on ``getContentResolver()`` like so: ``getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);`` and then I call ``createNewExcelFile(uri);``. – René Jörg Spies Apr 09 '20 at 12:09
  • 1
    I solved the problem by removing the call to ``takePersistableUriPersmission()`` and now it works perfectly fine. Thank you for your help. – René Jörg Spies Apr 09 '20 at 12:19