How to Get File Path from URI in Android Oreo (8.1) or above

ryosu picture ryosu · Oct 12, 2018 · Viewed 8.9k times · Source

Expected Behavior

When I am selecting the file which is stored inside "Download", it should able to retrieves its file name and path

Actual Behavior

When I am selecting the file which is stored inside "Download", it returns null.

Steps to Reproduce the Problem

  1. When picking file method is called, it displays the folders in Android
  2. Go to downloads, select a file
  3. It returns null in getting real path from URI

Here is the code what i implemented

public static String getPath(final Context context, final Uri uri) {
   String id = DocumentsContract.getDocumentId(uri);
   if (!TextUtils.isEmpty(id)) {
      if (id.startsWith("raw:")) {
          return id.replaceFirst("raw:", "");
      }
      try {
         final boolean isOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
         String stringContentURI;
         Uri contentUri;
         if(isOreo){ 
            stringContentURI = "content://downloads/my_downloads";
         }else{
            stringContentURI = "content://downloads/public_downloads";
         }
         contentUri = ContentUris.withAppendedId(
                    Uri.parse(stringContentURI), Long.valueOf(id));
         return getDataColumn(context, contentUri, null, null);
      } catch (NumberFormatException e) {
         return null;
      }
   }
}

public static String getDataColumn(Context context, Uri uri, String selection,
                                   String[] selectionArgs) {
    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {   column};
    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

However, it is working when selecting file from other folders in Android device

Please advise. Thanks everyone :)

Answer

ryosu picture ryosu · Oct 15, 2018

For now, the best approach for getting path is :

Getting physical file from URI as InputStream, ContentResolver.openInputStream() allow you to access the contents of a file without knowing its real path

String id = DocumentsContract.getDocumentId(uri);
InputStream inputStream = getContentResolver().openInputStream(uri);

then write it as a temporary file in cached storage

File file = new File(getCacheDir().getAbsolutePath()+"/"+id);
writeFile(inputStream, file);
String filePath = file.getAbsolutePath();

Here is the method to write temporary file into cached storage

void writeFile(InputStream in, File file) {
     OutputStream out = null;
     try {
          out = new FileOutputStream(file);
          byte[] buf = new byte[1024];
          int len;
          while((len=in.read(buf))>0){
            out.write(buf,0,len);
          }
     } catch (Exception e) {
          e.printStackTrace();
     }
     finally {
          try {
            if ( out != null ) {
                 out.close();
            }
            in.close();
          } catch ( IOException e ) {
               e.printStackTrace();
          }
     }
}

Not sure if its the best way to do, but the code is working properly :D