这个主题已经在这里的许多问题中讨论过了,大多数都有不同的结果,而且由于API的变化和URI的不同类型,没有明确的答案。
我自己也没有答案,但让我们来讨论一下。ExifInterface
有一个接受filePath
的构造函数。这本身就很烦人,因为现在不鼓励依赖路径-你应该使用Uri
s和ContentResolver
。好的。
名为uri
的Uri
可以从onActivityResult
中的Intent中检索(如果您使用ACTION_GET_CONTENT
从图库中选取图片),也可以是我们以前拥有的Uri
(如果您从相机中选取图片并调用intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
)。
空气污染API〈19
我们的uri
可以有两种不同的模式:
- 来自摄像头的URI大多数都有一个
file://
模式。这些模式很容易处理,因为它们包含路径。您可以调用new ExifInterface(uri.getPath())
,这样就完成了。 - 来自画廊或其他内容提供商的URI通常有一个
content://
接口,我个人不知道这是怎么回事,但这让我抓狂。
第二种情况,据我所知,应该用ContentResolver
来处理,你可以用Context.getContentResolver()
来处理。下面的适用于我测试过的所有应用程序,在任何情况下:
public static ExifInterface getPictureData(Context context, Uri uri) {
String[] uriParts = uri.toString().split(":");
String path = null;
if (uriParts[0].equals("content")) {
// we can use ContentResolver.
// let’s query the DATA column which holds the path
String col = MediaStore.Images.ImageColumns.DATA;
Cursor c = context.getContentResolver().query(uri,
new String[]{col},
null, null, null);
if (c != null && c.moveToFirst()) {
path = c.getString(c.getColumnIndex(col));
c.close();
return new ExifInterface(path);
}
} else if (uriParts[0].equals("file")) {
// it's easy to get the path
path = uri.getEncodedPath();
return new ExifInterface(path);
}
return null;
}
API19+
我的问题是从Kitkat开始的content://
URI。Kitkat引入了Storage Access Framework
(参见here)沿着一个新的intent ACTION_OPEN_DOCUMENT
和一个平台选择器。
在Android 4.4及更高版本上,您可以使用ACTION_OPEN_DOCUMENT Intent,该Intent会显示一个由系统控制的选取器UI,允许用户浏览其他应用提供的所有文件。用户可以从这一个UI中选取任何受支持的应用中的文件。
ACTION_OPEN_DOCUMENT不能替代ACTION_GET_CONTENT。您应根据应用的需要使用ACTION_GET_CONTENT。
为了简单起见,我们假设可以使用旧的ACTION_GET_CONTENT
:它将启动一个选择器对话框,您可以在其中选择一个图库应用程序。
然而,内容方法不再起作用了。有时候它在奇巧上起作用,但在棒棒糖上**从来就不起作用。我不知道到底发生了什么变化。
我已经寻找和尝试了很多;针对Kitkat专门采取的另一种方法是:
String wholeId = DocumentsContract.getDocumentId(uri);
String[] parts = wholeId.split(“:”);
String numberId = parts[1];
Cursor c = context.getContentResolver().query(
// why external and not internal ?
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{ col },
MediaStore.Images.Media._ID + “=?”,
new String[]{ numberId },
null);
这种方法有时有效,但有时无效。具体来说,当wholeId
类似于image:2839
时,它有效,但当wholeId
只是一个数字时,它显然无效。
您可以尝试使用系统选择器(例如,使用ACTION_OPEN_DOCUMENT
启动图库):如果你从“最近”中选择一张图片,它就起作用了;如果你从“下载”中选择一个图像,它就会坏掉。
那么该怎么做呢?!
直接的答案是你不,你在新版本的操作系统中找不到内容URI的文件路径。可以说,不是所有的内容URI都指向图片甚至文件。
这对我来说完全没问题,一开始我努力避免这种情况,但是,如果我们不应该使用路径,我们应该如何使用ExifInterface类?
我不明白现代应用程序是如何做到这一点的--找到方向和元数据是你马上要面对的问题,而ContentResolver
并没有提供任何这方面的API。你有ContentResolver.openFileDescriptor()
和类似的东西,但没有读取元数据的API(它确实在那个文件中)。可能有外部库从流中读取Exif
的东西,但我想知道解决这个问题的通用/平台方法。
我曾在谷歌的开源应用程序中搜索过类似的代码,但一无所获。
7条答案
按热度按时间5f0d552i1#
用一些示例代码来扩展alex.dorokhov的答案,支持库是一个很好的选择。
build.gradle
示例代码:
我之所以要这样做,是因为我们开始以api 25为目标(可能在24+上也有问题),但仍然支持回到api 19,在android 7上,如果我传入一个URI到相机,而这个URI只是引用一个文件,我们的应用会崩溃。因此,我必须创建一个URI,像这样传递到相机Intent。
这里的问题是,文件不可能将URI转换为实际的文件路径(除了保留临时文件路径)。
yduiuuwa2#
从内容URI(实际上是InputStream)获取EXIF现在在支持库中可用。https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html
aiqt4smr3#
以下代码适用于我测试过的所有应用程序,在任何情况下:
只有当
Uri
恰好是来自MediaStore
的东西时,它才会工作,如果Uri
恰好来自其他东西,它就会失败。直接的答案是,在较新版本的操作系统中,你找不到内容URI的文件路径。可以说,并非所有内容URI都指向图片甚至文件。
对,我在很多场合都指出过,比如在这里。
如果我们不应该使用路径,我们应该如何使用ExifInterface类?
您不需要。请使用其他代码获取EXIF头。
可能有外部库从流中读取Exif内容,但我想知道解决这个问题的公共/平台方法。
使用外部库。
我曾在谷歌的开源应用程序中搜索过类似的代码,但一无所获。
您将在the Mms app中找到一些。
ExifInterface
,支持使用InputStream
读入EXIF数据,对于Uri
标识的内容,可以通过ContentResolver
获取InputStream
。pgvzfuti4#
不要使用EXIF。你可以像这样从Uri获得图像的方向:
ecfdbz9o5#
安卓10 API 30
从图像URI获取Exif数据
d5vmydt96#
Dependency for AndroidX
要创建一个新示例,只需使用构造函数:
如果找不到依赖项,请将latest version添加到
gradle
文件(module: app
)yuvru6vn7#
在我的例子中,我在使用InputStream获取方向时遇到了问题。所以我使用了FileDescriptor,而不是从InputStream获取ExifInterface。
此解决方案不起作用:
当我为ExifInterface打开单独的InputStream时,我得到了更好的结果(但我不喜欢这样):
但我最终使用FileDescriptor来构造ExifInterface: