Home » Java » Create new file in the directory returned by Intent.ACTION_OPEN_DOCUMENT_TREE

Create new file in the directory returned by Intent.ACTION_OPEN_DOCUMENT_TREE

Posted by: admin December 28, 2021 Leave a comment

Questions:

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.

Answers:

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.