13

I am using the following code to get the file name and path of files in the File Manager. However it does not return a path for Google Drive files. Any idea how to obtain the actual path?

My code -

public String getFilePath() {
    if (uri.getScheme().equalsIgnoreCase("file")) {
        return uri.getLastPathSegment();
    }

    cursorLoader.setUri(uri);
    cursorLoader.setProjection(projections);
    Cursor cursor = cursorLoader.loadInBackground();
    int column_index = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA);
    cursor.moveToFirst();
    String realPath = cursor.getString(column_index);
    cursor.close();

    if (realPath == null || realPath.isEmpty()) {
        return null;
    }

return null;
}
jww
  • 97,681
  • 90
  • 411
  • 885
Suchi
  • 9,989
  • 23
  • 68
  • 112
  • 1
    Without looking into the details, my suspicion would be that those are not actually literal "files" on the device filesystem, but rather references to some information sharing scheme which might be used to obtain the contents. Do you have any hard evidence that they really are local files? – Chris Stratton Apr 14 '14 at 15:25
  • Agreed. There is not necessarily an "actual path" for any `Uri`. The code snippet that you are using will only work -- at best -- with files indexed by `MediaStore`. Applications like Google Drive are welcome to make file *content* available via `ACTION_GET_CONTENT`, `ACTION_PICK`, the storage access framework, etc. However, those files do not have to be anywhere that you can access them independently, such as having them on internal storage. – CommonsWare Apr 14 '14 at 15:25
  • It does have a content uri though - content://com.google.android.apps.docs.files/exposed_content/GzbCGuP7Bbpim%2FVz6FkjsA%3D%3D%0A%3BIaCw8dNKdKh2%2FIRHBzfQih86IT2FRnSTdSN6MS2d1L2UKC%2FuQyTlxUCOb%2Fu%2F0%2BlC%0A Doesn't this mean it is located somewhere on the device? – Suchi Apr 14 '14 at 15:26
  • 1
    "Doesn't this mean it is located somewhere on the device?" -- not necessarily. It could be a file that you do not have access to (e.g., internal storage for the Drive app). The `ContentProvider` could be decrypting an encrypted file on the fly and streaming that back to clients. The `ContentProvider` could be streaming it off of the Internet. And so on. There has never been a requirement that a `Uri` map to a file, which is why your `MediaStore` hack is unreliable for all versions of Android. Fewer `Uri` values will map to files on Android 4.4+ with the storage access framework. – CommonsWare Apr 14 '14 at 15:43
  • So its a dead end to get Google Drive files and store the path? is there a work-around? same problem here :) – Dennis Anderson Aug 21 '14 at 09:19

2 Answers2

10

You have to use the URI. Through the URI you can getContentResolver.query(theUriThatYouHave, null, null, null, null). Now that you have a cursor, you can check column names etc.

For Google drive there is a column name _display_name. This will give you the file name.

Now you want access to the file? You can open an InputStream to the URI via getContentResolver().openInputStream(theUriThatYouHave).

jww
  • 97,681
  • 90
  • 411
  • 885
clockwerk
  • 263
  • 3
  • 15
6

Get file name and real path from Google Drive URI very easily by following steps:

  1. Add file provider path in Android manifest file.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.demo.filemangerdemo">
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.STORAGE" />
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name="com.demo.filemangerdemo.activity.MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="${applicationId}.provider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/provider_paths"/>
            </provider>
        </application>
    </manifest>
    
  2. Create xml folder under res and add provider_paths.xml.

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <cache-path
            name="my_cache"
            path="." />
        <cache-path
            name="cache"
            path="." />
        <external-cache-path
            name="external_cache"
            path="." />
        <files-path
            name="files"
            path="." />
    </paths>
    
  3. Utils class for access data from Google Drive.

    public class Utils {
        private static Uri contentUri = null;
    
        @SuppressLint("NewApi")
        public static String getPath(final Context context, final Uri uri) {
            // check here to KITKAT or new version
            final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
            // DocumentProvider
            if (isKitKat && DocumentsContract.isDocumentUri(context, uri))
              {
                // MediaProvider
                 if (isMediaDocument(uri)) {
                     if (isGoogleDriveUri(uri)) {
                    return getDriveFilePath(uri, context);
                }
    
    
              }
          }
    
  4. isGoogleDriveUri method

    private static boolean isGoogleDriveUri(Uri uri) {
        return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
    }
    
  5. getDriveFilePath method

    private static String getDriveFilePath(Uri uri, Context context) {
        Uri returnUri = uri;
        Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
        /*
         * Get the column indexes of the data in the Cursor,
         *     * move to the first row in the Cursor, get the data,
         *     * and display it.
         * */
        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
        returnCursor.moveToFirst();
    
        String name = (returnCursor.getString(nameIndex));
        String size = (Long.toString(returnCursor.getLong(sizeIndex)));
        File file = new File(context.getCacheDir(), name);
        try {
            InputStream inputStream = context.getContentResolver().openInputStream(uri);
            FileOutputStream outputStream = new FileOutputStream(file);
            int read = 0;
            int maxBufferSize = 1 * 1024 * 1024;
            int bytesAvailable = inputStream.available();
    
            //int bufferSize = 1024;
            int bufferSize = Math.min(bytesAvailable, maxBufferSize);
    
            final byte[] buffers = new byte[bufferSize];
            while ((read = inputStream.read(buffers)) != -1) {
                outputStream.write(buffers, 0, read);
            }
            Log.e("File Size", "Size " + file.length());
            inputStream.close();
            outputStream.close();
            Log.e("File Path", "Path " + file.getPath());
        } catch (Exception e) {
            Log.e("Exception", e.getMessage());
        }
            return file.getPath();
        }     
    }
    
  6. Getting file path in onActivityResult

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
            if ((data != null) && (data.getData() != null)) {
                Uri selectedFile = data.getData();
                if (selectedFile.getLastPathSegment() != null) {
                    //Here you will get File Path
                    String strPath = FileUtils.getPath(this, selectedFile);  
                }
            }
        }
    }
    
Andreas
  • 2,455
  • 10
  • 21
  • 24
Satyawan Hajare
  • 1,112
  • 11
  • 10
  • Above code is working fine. we can get easily access file path from google drive using file provider and above mentioned steps. – Satyawan Hajare Apr 02 '19 at 07:24
  • Have you tested this for Google spreadsheets of your google drive? The code falls in exception. Please help if you have any solution. Thanks in advance. – Shailesh Sep 14 '20 at 11:16