Error in Android Studio with File Chooser in Download Folder

Viewed 1584

My Problem

I am currently developing an Android application with Android Studio. To choose a file the user clicks on a button, the explorer opens and he can select a file. For this process I used a helper class called FileChooser.Java. This whole system functions very well as long as the user doesn't select a file from the Download folder.

If the users tries to select a file from the Download folder this happens:

NumberFormatException: for input string: "msf:80123"

The URL the FileChooser returns: content://com.android.providers.downloads.documents/document/msf:80662

This is because the helper class expects a long type. Removing the msf: and adding the ID to the end doesn't work either. It seems like Android adds this msf: tag to everything in the Download folder.

I am looking for a valid solution to call files from the Download folder too.

The Call

Uri selectedFile = data.getData();
InputStream dataStream = new FileInputStream(FileChooser.getPath(getContext(), selectedFile));

Exception Call in FileChooser Class

 final Uri contentUri = ContentUris.withAppendedId(
       Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

Helper Class FileChooser

package com.example.dsvconverter.helper;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;


public class FileChooser {

    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @author paulburke
     */
    public static String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final String[] split = id.split(":");
                final String type = split[0];

                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    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 column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
}
2 Answers

In onActivityResult() :

InputStream dataStream = getContentResolver().openInputStream(data.getData());

Throw away that getPath() function.

This works for all uries.

The only problem is getting the path of the download directory or msf and NumberFormat exception, Try this one. It worked for me correctly

package com.example.bookingmelbourne;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileUtils {
    private static final String TAG = "FileUtils";
@WorkerThread
@Nullable
public static String getReadablePathFromUri(Context context, Uri uri) {

    String path = null;
    if ("file".equalsIgnoreCase(uri.getScheme())) {
        path = uri.getPath();
    }

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
        path = getPath(context, uri);
    }

    if (TextUtils.isEmpty(path)) {
        return path;
    }

    Log.d(TAG, "get path from uri: " + path);
    if (!isReadablePath(path)) {
        int index = path.lastIndexOf("/");
        String name = path.substring(index + 1);
        String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + name;
        if (copyFile(context, uri, dstPath)) {
            path = dstPath;
            Log.d(TAG, "copy file success: " + path);
        } else {
            Log.d(TAG, "copy file fail!");
        } 
    }
    return path;
}

public static String getPath(final Context context, final Uri uri) {
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            Log.d("External Storage", docId);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }
        } else if (isDownloadsDocument(uri)) {

            String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + getFileName(context,uri);

             if (copyFile(context, uri, dstPath)) {
                Log.d(TAG, "copy file success: " + dstPath);
                return dstPath;

            } else {
                Log.d(TAG, "copy file fail!");
            }


        } else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[]{split[1]};
            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    } else if ("content".equalsIgnoreCase(uri.getScheme())) {
        return getDataColumn(context, uri, null, null);
    } else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }
    return null;
}

public static String getFileName(Context context, Uri uri) {

    Cursor cursor = context.getContentResolver().query(uri,null,null,null,null);
    int nameindex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
    cursor.moveToFirst();

    return  cursor.getString(nameindex);
}


private 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 column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

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

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

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

private static boolean isReadablePath(@Nullable String path) {
    if (TextUtils.isEmpty(path)) {
        return false;
    }
    boolean isLocalPath;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        if (!TextUtils.isEmpty(path)) {
            File localFile = new File(path);
            isLocalPath = localFile.exists() && localFile.canRead();
        } else {
            isLocalPath = false;
        }
    } else {
        isLocalPath = path.startsWith(File.separator);
    }
    return isLocalPath;
}

private static boolean copyFile(Context context, Uri uri, String dstPath) {
    InputStream inputStream = null;
    OutputStream outputStream = null;
    try {
        inputStream = context.getContentResolver().openInputStream(uri);
        outputStream = new FileOutputStream(dstPath);

        byte[] buff = new byte[100 * 1024];
        int len;
        while ((len = inputStream.read(buff)) != -1) {
            outputStream.write(buff, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (outputStream != null) {
            try {
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    return true;
}

  }

After that, you can call this method passing context and uri objects

 String realPath = FileUtils.getReadablePathFromUri(context,uri)
Related