0

Here's what I would like to do in my app:

  • allow user to select files and store paths to them inside a database
  • display names of those files
  • allow user to click on filename and open the chosen file

So I'd like to get real path to file and store it. The user will only be allowed to get files from internal/external storage so, as I assume, the real path will always exist. I've been looking for solutions and pretty much everyone asks only about images/audio/video and uses MediaStore in their solutions (and I, as of now, have no idea what this is, and I can get Uri without using it). I'd like a solution that would work with any type of file, if that's possible.

Here's how I'm getting Uri:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("*/*");
startActivityForResult(Intent.createChooser(intent, "Choose File"), 1);

and I'm storing the received information in onActivityResult method.

So i get Uri and store it as a string which looks like, for example, content://com.android.providers.downloads.documents/document/4588. How do I convert it to a real filepath?

wubwubnoobnoob
  • 25
  • 1
  • 1
  • 7
  • It does not make much sense to store content schemes you get with ACTION_GET_CONTENT in a database as you are not permitted/abled to use them once your app has ended and restarted. – greenapps Feb 09 '18 at 14:32
  • @greenapps what would you suggest then if I need to kind of 'attach' files to an entry I'm using in the app? I don't need to actually save the file itself inside the app, just the path so that I can retrieve it, meaning I'd like to save the path and be able to open the file by that path. – wubwubnoobnoob Feb 09 '18 at 15:34
  • Well you have been told already that trying to obtain a file system path is a bad idea. So why are you continuing on that? With a content scheme you have access to the file too so there is not the least need for a file system path. So use the content scheme to retrieve the file. Just do that for testing purposes directly in onActivityResult(). – greenapps Feb 09 '18 at 15:37
  • `I need to kind of 'attach' files to an entry I'm using in the app?` Elaborate on that. Tell what you want to do. – greenapps Feb 09 '18 at 15:40
  • @greenapps I might've used the wrong words previously when I talked about paths, I apologize. This app is supposed to have homework entries where you can store stuff like subject, deadline, etc. I'd like to be able to 'attach' files of any kind to this h/w entry, so that when I'm displaying those entries, I could click on one and get a list of all attached files, then click on the chosen file and open it. That's why I need to store the way to the file in any way, be it filepath, Uri or something else. But I don't want to save the files itself with the app, just the way to access them. – wubwubnoobnoob Feb 09 '18 at 15:50
  • No. Again: You do not need a file path for that. You can use the content scheme itself to open a file. Just try in onActivityResult() first. – greenapps Feb 09 '18 at 15:53
  • @greenapps sorry, I can't understand you yet. How would I open an 'attached' file after I've, for example, closed the app and opened it again? I need to know something about the 'attached' files, right? – wubwubnoobnoob Feb 09 '18 at 17:02
  • You would open it in the same way as you were assumed to have tested already in onActivityResult using that content scheme. – greenapps Feb 09 '18 at 17:08

2 Answers2

2

Sharing my Fileutils file from one of my projects

public class FileUtils {

public static String getFilePath(Context context, Uri uri) throws URISyntaxException {
    String selection = null;
    String[] selectionArgs = null;
    // Uri is different in versions after KITKAT (Android 4.4), we need to
    if (Build.VERSION.SDK_INT >= 19 && DocumentsContract.isDocumentUri(context
            .getApplicationContext(), uri)) {
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            return Environment.getExternalStorageDirectory() + "/" + split[1];
        } else if (isDownloadsDocument(uri)) {
            final String id = DocumentsContract.getDocumentId(uri);
            uri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
        } else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            if ("image".equals(type)) {
                uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }
            selection = "_id=?";
            selectionArgs = new String[]{
                    split[1]
            };
        } else if (isGoogleDriveFile(uri)) {
            String mimeType = context.getContentResolver().getType(uri);
            Cursor returnCursor =
                    context.getContentResolver().query(uri, null, null, null, null);
            int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
            int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
            returnCursor.moveToFirst();
            String fileName = returnCursor.getString(nameIndex);
            Log.d("name", returnCursor.getString(nameIndex));
            Log.d("size", Long.toString(returnCursor.getLong(sizeIndex)));
            try {
                InputStream inputStream = context.getContentResolver().openInputStream(uri);

                File root = new File(Environment.getExternalStorageDirectory(), "WittyParrot");
                root.mkdirs();
                File file = new File(root, fileName);
                copyStreamToFile(file, inputStream);
                return Uri.fromFile(file).getPath();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                returnCursor.close();

            }

        }
    }

    if ("content".equalsIgnoreCase(uri.getScheme())) {
        String[] projection = {
                MediaStore.Images.Media.DATA
        };
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver()
                    .query(uri, projection, selection, selectionArgs, null);
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            if (cursor.moveToFirst()) {
                return cursor.getString(column_index);
            }
        } catch (Exception e) {
        }
    } else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }
    return null;
}

public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

public static boolean isGoogleDriveFile(Uri uri) {
    return "com.google.android.apps.docs.storage".equals(uri.getAuthority());
}

public static void copyStreamToFile(File file, InputStream input) {
    try {
        OutputStream output = new FileOutputStream(file);
        try {
            byte[] buffer = new byte[4 * 1024]; // or other buffer size
            int read;

            while ((read = input.read(buffer)) != -1) {
                output.write(buffer, 0, read);
            }

            output.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            output.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
}
karandeep singh
  • 2,294
  • 1
  • 15
  • 22
  • 1
    Because it is wrong. It is flawed code that you copied from somebody else. It makes assumptions about the internal implementation of various providers, none of which may hold up over time. It assumes that you have read access to all files, which is wrong (see: removable storage). Worse, it assumes that all `Uri` values come from one of these sources, which is *very* wrong. Programmers are perfectly capable of using `FileProvider` or creating their own `ContentProvider`, returning a `Uri` that points to those, and your approach simply fails for them. A `Uri` is not a file. – CommonsWare Feb 09 '18 at 12:55
  • I have actually used this code in one of my projects. nevertheless, thank you for your response. – karandeep singh Feb 09 '18 at 12:57
0

The user will only be allowed to get files from internal/external storage

Not according to your code. The user can use anything that supports ACTION_GET_CONTENT. There is no requirement for ACTION_GET_CONTENT implementations to be limited to external storage, and you cannot access internal storage from other apps using the filesystem.

If you are only willing to work with external storage, use a file chooser library, not ACTION_GET_CONTENT or ACTION_OPEN_DOCUMENT.

I'd like a solution that would work with any type of file, if that's possible.

Use a file chooser library.

How do I convert it to a real filepath?

You don't.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491