35

In Nougat, this function is not working.

String path = getRealPathFromURI(this, getIntent().getParcelableExtra(Intent.EXTRA_STREAM));


public String getRealPathFromURI(Context context, Uri contentUri) {
    Cursor cursor = null;
    try {
        String[] proj = {MediaStore.Images.Media.DATA};
        cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
        if (cursor == null) return contentUri.getPath();
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

Crash log:

java.lang.RuntimeException: Unable to start activity ComponentInfo{class path}: java.lang.IllegalArgumentException: column '_data' does not exist
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2659)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2724)
   at android.app.ActivityThread.-wrap12(ActivityThread.java)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1473)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:154)
   at android.app.ActivityThread.main(ActivityThread.java:6123)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by java.lang.IllegalArgumentException: column '_data' does not exist
   at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:333)
   at android.database.CursorWrapper.getColumnIndexOrThrow(CursorWrapper.java:87)
   at com.package.SaveImageActivity.getRealPathFromURI()
   at com.package.SaveImageActivity.onCreate()
   at android.app.Activity.performCreate(Activity.java:6672)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1140)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2612)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2724)
   at android.app.ActivityThread.-wrap12(ActivityThread.java)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1473)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:154)
   at android.app.ActivityThread.main(ActivityThread.java:6123)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)

This function is working properly in devices before Android N. I read the article file:// scheme is now not allowed to be attached with Intent on targetSdkVersion 24 (Android Nougat). But couldn't find any solution. So please help.

Sergey Glotov
  • 20,200
  • 11
  • 84
  • 98
vidha
  • 1,257
  • 2
  • 14
  • 22
  • 1
    Why are you trying to get a file path from the URI? Generally you should try to work with the URI as a stream because not all URIs are backed by a local file with a path. If your trying to get access to the file for the purpose of reading exif data then you could open it as a stream and use this support library to be able to read exif data. https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html – startoftext Mar 21 '17 at 19:53
  • @startoftext I know this is a late ask, but with your approach, there are various devices that show ORIENTATION_UNDEFINED for the image, and they can be either landscape or portrait, depending on the API version. For API 26 (Android 8) on an emulator, it gets rotated 90 degrees, whereas on API 29 and above (Android 9 and above) it shows a portrait image, without any rotation. Do you have any solution on that? – Harshit Saxena Aug 23 '21 at 14:44

5 Answers5

51

As per given answer by CommonsWare, the solution code is :

public static String getFilePathFromURI(Context context, Uri contentUri) {
    //copy file and send new file path 
    String fileName = getFileName(contentUri);
    if (!TextUtils.isEmpty(fileName)) {
        File copyFile = new File(TEMP_DIR_PATH + File.separator + fileName);
        copy(context, contentUri, copyFile);
        return copyFile.getAbsolutePath();
    }
    return null;
}

public static String getFileName(Uri uri) {
    if (uri == null) return null;
    String fileName = null;
    String path = uri.getPath();
    int cut = path.lastIndexOf('/');
    if (cut != -1) {
        fileName = path.substring(cut + 1);
    }
    return fileName;
}

public static void copy(Context context, Uri srcUri, File dstFile) {
    try {
        InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
        if (inputStream == null) return;
        OutputStream outputStream = new FileOutputStream(dstFile);
        IOUtils.copyStream(inputStream, outputStream);
        inputStream.close();
        outputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

I hope this helps you.

The origin of IOUtils.copy is from this site : https://www.developerfeed.com/copy-bytes-inputstream-outputstream-android/ (May need to change a bit the exception but it works as needed)

vidha
  • 1,257
  • 2
  • 14
  • 22
24

This function is working properly in devices before Android N

It works for very few Uri values, may not have a result (e.g., for things that are indexed by MediaStore that are not local files), and may not have a usable result (e.g., for files on removable storage).

So please help.

Use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri. Ideally, just use that stream directly, for whatever it is that you are trying to do. Or, use that InputStream and some FileOutputStream on a file that you control to make a copy of the content, then use that file.

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

//The following code is working in Android N:

private static String getFilePathForN(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.getFilesDir(), 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());
        Log.e("File Size", "Size " + file.length());
    } catch (Exception e) {
        Log.e("Exception", e.getMessage());
    }
    return file.getPath();
}
Markoorn
  • 2,477
  • 1
  • 16
  • 31
Satyawan Hajare
  • 1,112
  • 11
  • 10
3

@Redturbo I can't write a comment, I write here

IOUtils.copyStream(inputStream, outputStream);

TEMP_DIR_PATH - any your directory path, something like this

 File rootDataDir = context.getFilesDir();
 File copyFile = new File( rootDataDir + File.separator + fileName + ".jpg");
kalmahik
  • 31
  • 3
0

Further adding to Vidha's answer as per Android R storage framework guide and if you do not want to permanently save the copied file to storage:

    public static String getFilePathFromURI(Context context, Uri contentUri) {
    File folder;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        folder = new File (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)+ "/"+ "yourAppFolderName" );
    } else {
        folder = new File(Environment.getExternalStorageDirectory() + "/"+ "yourAppFolderName");
    }
    // if you want to save the copied image temporarily for further process use .TEMP folder otherwise use your app folder where you want to save
    String TEMP_DIR_PATH = folder.getAbsolutePath() + "/.TEMP_CAMERA.xxx";
    //copy file and send new file path
    String fileName = getFilename();
    if (!TextUtils.isEmpty(fileName)) {
        File dir = new File(TEMP_DIR_PATH);
        dir.mkdirs();
        File copyFile = new File(dir, fileName);
        copy(context, contentUri, copyFile);
        return copyFile.getAbsolutePath();
    }
    return null;
}

public static void copy(Context context, Uri srcUri, File dstFile) {
    try {
        InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
        if (inputStream == null) return;
        OutputStream outputStream = new FileOutputStream(dstFile);
        IOUtils.copyStream(inputStream, outputStream);
        inputStream.close();
        outputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static String getFilename() {
    // if file is not to be saved, this format can be used otherwise current fileName from Vidha's answer can be used
    String ts = (new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)).format(new Date());
    return ".TEMP_" + ts + ".xxx";
}