从Uri中获取真实路径 - 在安卓Q中,DATA已被弃用

19

我成功地实现了从相册中检索图像的方法,通过ACTION_PICK意图返回的Uri来获取真实路径。以下是示例:

// getRealPathFromURI(intent.getData());

private String getRealPathFromURI(Uri contentURI) {
    String result;
    Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
    if (cursor == null) { // Source is Dropbox or other similar local file path
        result = contentURI.getPath();
    } else { 
        cursor.moveToFirst(); 
        int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 
        result = cursor.getString(idx);
        cursor.close();
    }
    return result;
}

就像这个 答案 一样。

最近将 compileSdkVersion 更新为 29,显然大家都在使用的 DATA 属性已经被弃用了。在官方文档中,他们建议使用 FileDescriptor,问题是我不知道具体怎么做。
我找到的唯一一件事是这个 问题。但是那里没有找到合适的答案。

请帮帮我用推荐的方式或其他方式解决这个废弃的问题。

谢谢。


更新:

按照@CommonsWare答案,将用户选择的图像返回的Uri复制到本地目录,使用context.getContentResolver.openInputStream(Uri)

甚至尝试从Google Drive检索文件-它也可以工作。唯一的问题是需要很长时间(对于5MB的文件大约需要20秒)。

额外的好处是,我被允许删除外部存储权限,这是不需要使用应用程序的本地目录的。

对我来说没有外部路径了!


1
尝试仅复制具有URI对象的数据。正如@CommonsWare所说,FileOutputStream(yourFullPath).use { oStream -> contentResolver.openInputStream(uri)?.use { iStream -> oStream.write(iStream.readBytes()) } } - Stanley Wintergreen
2个回答

21

这个问题对我也在一周前出现过。

我的解决方案是从URI创建一个InputStream,然后通过复制输入流的内容来创建一个OutputStream

注意:你可以使用异步调用来调用此方法,因为复制非常大的文件可能会有一些延迟,你不想阻塞你的UI。

@Nullable
public static String createCopyAndReturnRealPath(
       @NonNull Context context, @NonNull Uri uri) {
    final ContentResolver contentResolver = context.getContentResolver();
    if (contentResolver == null)
        return null;

    // Create file path inside app's data dir
    String filePath = context.getApplicationInfo().dataDir + File.separator
            + System.currentTimeMillis();

    File file = new File(filePath);
    try {
        InputStream inputStream = contentResolver.openInputStream(uri);
        if (inputStream == null)
            return null;

        OutputStream outputStream = new FileOutputStream(file);
        byte[] buf = new byte[1024];
        int len;
        while ((len = inputStream.read(buf)) > 0)
            outputStream.write(buf, 0, len);

        outputStream.close();
        inputStream.close();
    } catch (IOException ignore) {
        return null;
    }

    return file.getAbsolutePath();
}

1
绝对是一个超级解决方案。 - Sanush
1
救了我的一天...它可以选择所有文件运行..谢谢... - Shashi

11
我正在成功地实现一种方法,通过ACTION_PICK意图返回的Uri从图库中检索图像的真实路径。但该代码可能并不适用于所有图像。没有要求DATA指向您可以访问的文件系统路径。FWIW, this是我对那个问题的答案。那个技术并不特别好,也不再起作用,因为Android已经锁定了/ proc。在官方文档中,他们建议使用FileDescriptor,问题是我不知道具体如何操作。更一般的概念是使用ContentResolver来处理Uri,无论你是否获得InputStream(openInputStream())、OutputStream(openOutputStream())或FileDescriptor。使用这些内容消耗。如果您有一些绝对需要文件的API,请将内容(例如,从InputStream)复制到您控制的文件中(例如,在getCacheDir()中)。作为奖励,现在您的代码也可以使用存储访问框架(例如ACTION_OPEN_DOCUMENT)和互联网(例如OkHttp),如果和当需要的话。

对于我的应用程序,我需要播放音频文件的路径,这个路径我从MediaStore.Audio.Media.DATA获取,但是MediaStore.Audio.Media._ID列不足以获取uri吗?然后我使用ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id),这样它就会返回内容uri,然后我可以在mediaPlayer.setDataSource中使用它。但出现了“无法解析方法setDataSource(android.net.Uri)”的错误。 - Vince VD
1
@VinceVD:它是setDataSource(Context, Uri):https://developer.android.com/reference/android/media/MediaPlayer#setDataSource(android.content.Context,%20android.net.Uri) - CommonsWare
1
@VinceVD:“我刚在你的博客上发现了这个”--请使用复制和粘贴引用,因为您没有正确获取此引用。它是“即使您获得了MediaStore Uri,那也可能是索引可移动存储器上的媒体,而您无法直接访问该文件系统。”。“那么这是否意味着当我从我的SD卡播放歌曲时不起作用?”--MediaStore可以直接访问文件系统中的媒体。您的应用程序没有。如果您正在使用MediaStore Uri,则MediaStore是访问文件系统的代码,而不是您的应用程序。 - CommonsWare
@VinceVD:很遗憾,不行。那只是从“MediaStore”表中删除条目。我不知道使用“MediaStore”删除内容的方法。 - CommonsWare
@VinceVD:“那么这是否意味着只使用内容URI就无法删除此文件?”--正如我所写的,我不知道你该如何做到。 - CommonsWare
显示剩余3条评论

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