从URI获取文件路径

9

我有一个图像文件的Uri。

我使用以下代码从Uri获取文件路径:

public String getRealPathFromURI(Uri contentUri) {
    Cursor cursor = null;
    try {
        String[] proj = { MediaStore.Images.Media.DATA };
        cursor = mContext.getContentResolver().query(contentUri, proj, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } catch (Exception e) {
        Log.message(e.getMessage());
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    return null;
}

如果我从图库应用程序中选择图像(Android 4.4.2,arm-emu),
uri.getPath = /external/images/media/16 and it work's fine (My file path: /storage/sdcard/Download/0_cf15a_7800a7e5_orig.jpg)

如果我从文档应用程序(Android 4.4.2,arm-emu)选择图像,则:
 I have uri.getPath = /document/image:16 and function getRealPathFromURI returns null.

我可以如何返回适用于两种操作的正确文件路径?

我的代码是:

    @Override
    public void onClick(View v) {
        final File root = new File(Environment.getExternalStorageDirectory() + File.separator + "Photohunt" + File.separator);
        root.mkdirs();
        final String fname = Common.getUniqueImageFilename();
        final File sdImageMainDirectory = new File(root, fname);
        outputFileUri = Uri.fromFile(sdImageMainDirectory);
            // Camera.
            final List<Intent> cameraIntents = new ArrayList<Intent>();
            final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            final PackageManager packageManager = mContext.getPackageManager();
            final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
            for(ResolveInfo res : listCam) {
                final String packageName = res.activityInfo.packageName;
                final Intent intent = new Intent(captureIntent);
                intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
                intent.setPackage(packageName);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
                cameraIntents.add(intent);
            }

            // Filesystem.
            final Intent galleryIntent = new Intent();
            galleryIntent.setType("image/*");
            galleryIntent.setAction(Intent.ACTION_GET_CONTENT);

            // Chooser of filesystem options.
            final Intent chooserIntent = Intent.createChooser(galleryIntent, "Select Source");

            // Add the camera options.
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));

            startActivityForResult(chooserIntent, PICTURE_REQUEST_CODE);
    }

处理活动结果:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if(resultCode == Activity.RESULT_OK)
    {
        if(requestCode == PICTURE_REQUEST_CODE)
        {
            final boolean isCamera;
            if(data == null)
            {
                isCamera = true;
            }
            else
            {
                final String action = data.getAction();
                if(action == null)
                {
                    isCamera = false;
                }
                else
                {
                    isCamera = action.equals(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                }
            }

            Uri selectedImageUri;
            if(isCamera)
            {
                selectedImageUri = outputFileUri;
            }
            else
            {
                selectedImageUri = data == null ? null : data.getData();
            }

            Log.variable("uri", selectedImageUri.getPath());
            ConfirmImageFragment fragment = new ConfirmImageFragment(selectedImageUri, mContestId);
            FragmentTransaction transaction = getSherlockActivity().getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.main_container, fragment);
            transaction.addToBackStack(null);
            transaction.commit();
        }
    }

    super.onActivityResult(requestCode, resultCode, data);
}

将选定的文件加载到ImageView中对两种状态都能正常工作:

private void loadImage() {
    try {
        Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), mUri);
        mImage.setImageBitmap(bitmap);
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
7个回答

23

解决方案:

    public class RealPathUtil {

    @SuppressLint("NewApi")
    public static String getRealPathFromURI_API19(Context context, Uri uri){
        String filePath = "";
        String wholeID = DocumentsContract.getDocumentId(uri);

         // Split at colon, use second item in the array
         String id = wholeID.split(":")[1];

         String[] column = { MediaStore.Images.Media.DATA };     

         // where id is equal to             
         String sel = MediaStore.Images.Media._ID + "=?";

         Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                                   column, sel, new String[]{ id }, null);

         int columnIndex = cursor.getColumnIndex(column[0]);

         if (cursor.moveToFirst()) {
             filePath = cursor.getString(columnIndex);
         }   
         cursor.close();
         return filePath;
    }


    @SuppressLint("NewApi")
    public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
          String[] proj = { MediaStore.Images.Media.DATA };
          String result = null;

          CursorLoader cursorLoader = new CursorLoader(
                  context, 
            contentUri, proj, null, null, null);        
          Cursor cursor = cursorLoader.loadInBackground();

          if(cursor != null){
           int column_index = 
             cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
           cursor.moveToFirst();
           result = cursor.getString(column_index);
          }
          return result;  
    }

    public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri){
               String[] proj = { MediaStore.Images.Media.DATA };
               Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
               int column_index
          = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
               cursor.moveToFirst();
               return cursor.getString(column_index);
    }
}

http://hmkcode.com/android-display-selected-image-and-its-real-path/


DocumentsContract.getDocumentId(uri); 未解决,如何组织导入DocumentsContract? - Madhu
你如何为非图片文件实现这个呢? - John Ernest Guadalupe
6
耶稣。安卓API太糟糕了。 - shoke
甚至都不能工作。在三星S2上使用标准的相册应用程序。 - Oliver Dixon
@Artem 尝试了适用于 Kitkat 及以上版本的代码。但对于 URI:content://com.android.providers.media.documents/document/image%3A63829 无效。我是在 Motorola Nexus 6 的文件选择器中选择了这张图片。 - Jimit Patel
它只适用于媒体。那其他文件呢?像这样的URI:"/document/135" - Amir Ziarati

2
检测API级别并使用正确的方法的更简单的答案版本如下:
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getFilePath(Context context, Uri uri)
{
    int currentApiVersion;
    try
    {
         currentApiVersion = android.os.Build.VERSION.SDK_INT;
    }
    catch(NumberFormatException e)
    {
        //API 3 will crash if SDK_INT is called
        currentApiVersion = 3;
    }
    if (currentApiVersion >= Build.VERSION_CODES.KITKAT)
    {
        String filePath = "";
        String wholeID = DocumentsContract.getDocumentId(uri);

        // Split at colon, use second item in the array
        String id = wholeID.split(":")[1];

        String[] column = {MediaStore.Images.Media.DATA};

        // where id is equal to
        String sel = MediaStore.Images.Media._ID + "=?";

        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                column, sel, new String[]{id}, null);

        int columnIndex = cursor.getColumnIndex(column[0]);

        if (cursor.moveToFirst())
        {
            filePath = cursor.getString(columnIndex);
        }
        cursor.close();
        return filePath;
    }
    else if (currentApiVersion <= Build.VERSION_CODES.HONEYCOMB_MR2 && currentApiVersion >= Build.VERSION_CODES.HONEYCOMB)

    {
        String[] proj = {MediaStore.Images.Media.DATA};
        String result = null;

        CursorLoader cursorLoader = new CursorLoader(
                context,
                uri, proj, null, null, null);
        Cursor cursor = cursorLoader.loadInBackground();

        if (cursor != null)
        {
            int column_index =
                    cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            result = cursor.getString(column_index);
        }
        return result;
    }
    else
    {

        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
        int column_index
                = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    }
}

2
如何在非图像文件中实现这个功能? - John Ernest Guadalupe

2

以下是我使用的解决方案,包含我目前找到的所有技巧,我也在自己的应用程序中使用 (这里) :

FileUtilEx.kt

fun Closeable?.closeSilently() {
    if (this != null) try {
        this.close()
    } catch (e: Exception) {
    }
}

object FileUtilEx {
    fun getFilePathFromUri(context: Context, uri: Uri, includeUriMappingTechnique: Boolean = true, tryToGetWritePermission: Boolean = false): ClosableFileHolder? {
        var file: File
        uri.path?.let {
            file = File(it)
            if (file.exists() && file.canRead()) {
//                Log.d("AppLog", "got real file")
                return ClosableFileHolder(file)
            }
        }
        if (uri.scheme == "file") {
            try {
                val jUri = java.net.URI(uri.scheme, uri.schemeSpecificPart, uri.fragment)
                file = File(jUri)
                if (file.exists() && file.canRead()) {
//                    Log.d("AppLog", "got real file")
                    return ClosableFileHolder(file)
                }
            } catch (e: Exception) {
            }
            try {
                val uriStr = uri.toString()
                val decodePath = URLDecoder.decode(uriStr, "UTF-8")
                file = File(decodePath)
                if (file.exists() && file.canRead()) {
//                    Log.d("AppLog", "got real file")
                    return ClosableFileHolder(file)
                }
            } catch (e: UnsupportedEncodingException) {
            }
        }
        val authority = uri.authority
        if (uri.scheme == "content") {
            //handles "Files" app, and many others
            getRealPathFromUri(context, uri)?.let {
                file = File(it)
                if (file.exists() && file.canRead()) {
//                    Log.d("AppLog", "got real file")
                    return ClosableFileHolder(file)
                }
            }
        }
        if (includeUriMappingTechnique) {
            val fileUsingUriMappingTechnique = getFileUsingUriMappingTechnique(context, uri, tryToGetWritePermission)
            if (fileUsingUriMappingTechnique != null)
                return fileUsingUriMappingTechnique
        }
        getFilePathFromDocumentUri(context, uri)?.let { filePath ->
            file = File(filePath)
            if (file.exists() && file.canRead())
                return ClosableFileHolder(file)
        }
        return null
    }


    private fun getRealPathFromUri(context: Context, contentUri: Uri): String? {
        try {
            context.contentResolver.query(contentUri, arrayOf(MediaStore.Images.Media.DATA), null, null, null)?.use { cursor ->
                val columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
                if (columnIndex < 0 || cursor.count <= 0)
                    return null
                cursor.moveToFirst()
                return cursor.getString(columnIndex)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    fun getFileUsingUriMappingTechnique(context: Context, androidUri: Uri, tryToGetWritePermission: Boolean = false): ClosableFileHolder? {
        var parcelFileDescriptor: ParcelFileDescriptor? = null
        for (i in 0..1)
            try {
                parcelFileDescriptor = context.contentResolver.openFileDescriptor(androidUri, if (tryToGetWritePermission && i == 0) "w" else "r")
                if (parcelFileDescriptor != null) {
                    val fd: Int = parcelFileDescriptor.fd
                    val linkFileName = "/proc/self/fd/$fd"
                    if (VERSION.SDK_INT >= VERSION_CODES.O) {
                        val realFilePath = Files.readSymbolicLink(Paths.get(linkFileName))
                        val file = realFilePath.toFile()
                        if (file.exists() && file.canRead()) {
                            parcelFileDescriptor.closeSilently()
                            return ClosableFileHolder(file)
                        }
                    }
                    val file = File(linkFileName)
                    if (file.exists() && file.canRead())
                        return ClosableFileHolder(file, parcelFileDescriptor)
                    parcelFileDescriptor.closeSilently()
                }
            } catch (e: Exception) {
                parcelFileDescriptor.closeSilently()
                parcelFileDescriptor = null
            }
        return null
    }

    private fun getFilePathFromDocumentUri(context: Context, uri: Uri): String? {
        //            https://dev59.com/9W035IYBdhLWcg3wC7cR
        // DocumentProvider
        if (VERSION.SDK_INT >= VERSION_CODES.KITKAT && DocumentFile.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if ("com.android.externalstorage.documents" == uri.authority) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                val type = split[0]
                // This is for checking Main Memory
                return if ("primary".equals(type, ignoreCase = true)) {
                    if (split.size > 1) {
                        Environment.getExternalStorageDirectory().absolutePath + "/" + split[1] + "/"
                    } else {
                        Environment.getExternalStorageDirectory().absolutePath + "/"
                    }
                    // This is for checking SD Card
                } else {
                    "storage" + "/" + docId.replace(":", "/")
                }
            }
        }
        return null
    }
}

ClosableFileHolder.kt

class ClosableFileHolder(val file: File, private val parcelFileDescriptor: ParcelFileDescriptor? = null) : Closeable {

    fun isUsingUriMappingTechnique(): Boolean = parcelFileDescriptor != null

    override fun close() {
        parcelFileDescriptor.closeSilently()
    }

    protected fun finalize() {
        parcelFileDescriptor.closeSilently()
    }
}

使用方法:

FileUtilEx.getFilePathFromUri(context, uri, false)?.use {
    val file = it.file
    ...
}

tryToGetWritePermission参数用于在您想要访问文件但通常不可用的情况下(例如在SAF中),将其提供给您的另一个位置。如果您想要访问文件但不关心它实际存在的位置,则此选项非常有用。


太好了,它在所有类型的测试用例中都能正常工作。谢谢。 - Vishal Thakkar
@VishalThakkar,你能否在特殊情况下检查一下,比如SD卡?我尽可能地使用了所有的东西... - android developer
我没有带有SD卡的设备。顺便问一下,你能提供Java版本的上述代码吗? - Vishal Thakkar
@VishalThakkar Java也是类似的。你也可以通过Java调用这些函数,无需转换。 - android developer
我该如何在Java中调用这个函数?据我所知,扩展函数无法在Java中调用。 - Vishal Thakkar
这里没有你需要调用的扩展函数(顺便说一下,你可以调用它们,只是更麻烦)。看看我写的“用法”部分... - android developer

1

请确保在清单文件中授权。 没有授权就尝试各种方法浪费了2个小时 :/

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

0

所以我尝试了堆栈上的基本上每个答案,但这是唯一一个对我有用的。重要的是将URI视为指向图像存在的“承诺”。这并不意味着该位置有文件,但如果您正确地提出请求,您将获得图像。attachementPath是图像的路径(可以像常规文件一样使用)。请参见以下内容:

try {
            InputStream input = getActivity().getContentResolver().openInputStream(imageUri);
            File file = new File(getActivity().getCacheDir(), "cacheFileAppeal.png");
            try {

                OutputStream output = new FileOutputStream(file);
                try {
                    try {
                        byte[] buffer = new byte[4 * 1024]; // or other buffer size
                        int read;

                        if (input != null) {
                            while ((read = input.read(buffer)) != -1) {
                                output.write(buffer, 0, read);
                            }
                        }
                        output.flush();
                    } finally {
                        output.close();
                        attachmentPath = file.getAbsolutePath();
                    }
                } catch (Exception e) {
                    e.printStackTrace(); // handle exception, define IOException and others
                }

            } finally {
                try {
                    if (input != null) {
                        input.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

0

我认为这应该可以工作。一个通用的从路径获取URI的代码:

String path = yourAndroidURI.uri.getPath()
File file = new File(new URI(path));

4
异常:URI不是绝对路径:/document/image:16。 - Artem

0

公共静态整型函数 getIdFromUri(Uri uri, String constantId) { Cursor c = ContentMaster.getContentResolver().query(uri, new String[]{constantId}, null, null, null);

    int res = -1;
    if(c != null && c.getCount() > 0)
    {
        c.moveToFirst();
        res = c.getInt(0);
        c.close();
    }
    return res;
}
public static Uri getUriWithId(Uri baseUri,int contactId)
{
    Uri u = ContentUris.withAppendedId(baseUri, contactId);

    if(u != null)
        return u;

    return Uri.EMPTY;
}
public static String getRealPathFromURI(Uri uri)
{
    if(uri == null)
        return "";

    if ("file".equalsIgnoreCase(uri.getScheme()))
        return uri.getPath();


    String[] pro = { MediaStore.Images.Media.DATA };
    String result = null;
    Cursor cursor;

    if(VERSION.SDK_INT > 18)
    {
        //String wholeID = DocumentsContract.getDocumentId(uri);
        //String id = wholeID.split(":")[1];
        String id = String.valueOf(getIdFromUri(uri, Images.Media._ID));
        String[] column = { MediaStore.Images.Media.DATA };
        String where = MediaStore.Images.Media._ID + "=?";
        Uri u = uri;

        if(isMediaDocument(uri))
        {
            if (getUriMediaDocumentType(uri).equals("image"))
            {
                if(isExternalStorageDocument(uri))
                    u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                else
                    u = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
            }
            else if (getUriMediaDocumentType(uri).equals("video"))
            {
                if(isExternalStorageDocument(uri))
                    u = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                else
                    u = MediaStore.Video.Media.INTERNAL_CONTENT_URI;
            }
            else if (getUriMediaDocumentType(uri).equals("audio"))
            {
                if(isExternalStorageDocument(uri))
                    u = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                else
                    u = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
            }
        }
        else if(isDownloadsDocument(uri))
        {
            u = getUriWithId(Uri.parse("content://downloads/public_downloads"), Integer.getInteger(id));
        }

        cursor = getContentResolver().query(u, column, where, new String[]{id}, null);
    }
    else if(VERSION.SDK_INT > 10)
    {
        CursorLoader cursorLoader = new CursorLoader(SystemMaster.getContext(), uri, pro, null, null, null);
        cursor = cursorLoader.loadInBackground();
    }
    else
    {
        cursor = SystemMaster.getContext().getContentResolver().query(uri, pro, null, null, null);
    }


    if(cursor != null && cursor.getCount() > 0)
    {
        cursor.moveToFirst();
        result = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        cursor.close();
    }

    return result;
}

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 String getUriMediaDocumentType(Uri uri)
{
    if(isMediaDocument(uri))
    {
       //TODO
        return "image";
    }

    return "";
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接