有关如何从URI获取Exif数据的最终答案

这个主题已经在这里讨论了很多问题,结果大不相同,由于API的变化和不同types的URI, 没有明确的答案

我自己没有答案,但我们来谈谈。 ExifInterface有一个接受filePath构造函数。 这本身是令人讨厌的,因为现在不鼓励依赖path – 你应该使用UriContentResolver 。 好。

我们可以从onActivityResult (如果从ACTION_GET_CONTENT从图库中select图片)的意图中获取我们的Uri命名的uri或者可以是我们以前拥有的Uri (如果您从相机中选取图片并调用intent.putExtra(MediaStore.EXTRA_OUTPUT, uri) )。

API <19

我们的uri可以有两种不同的模式:

  • 来自相机的Uris大多会有一个file:// schema。 那些很容易对待,因为他们持有的path。 你可以调用new ExifInterface(uri.getPath()) ,你就完成了。
  • 来自画廊或其他内容提供商的Uris通常具有content://接口。 我个人不知道这是关于什么的,但却让我发疯。

据我所知,第二种情况应该使用Context.getContentResolver()获得的ContentResolver处理。 以下内容适用于我testing过的所有应用程序,无论如何:

 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 (参见这里 )以及一个新的intent, ACTION_OPEN_DOCUMENT和一个平台select器。 但是,据说

在Android 4.4及更高版本中,您可以select使用ACTION_OPEN_DOCUMENT意图,该意图显示由系统控制的选取器UI,用户可以浏览其他应用程序提供的所有文件。 从这个单一的用户界面,用户可以从任何支持的应用程序中挑选一个文件。

ACTION_OPEN_DOCUMENT不打算替代ACTION_GET_CONTENT。 你应该使用的取决于你的应用程序的需求。

所以为了保持这个非常简单,让我们说,我们可以用旧的ACTION_GET_CONTENT :它会触发一个select器对话框,您可以select一个图库应用程序。

但是,内容方法不再适用。 有时它适用于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只是一个数字的时候显然会中断。

您可以使用系统select器(即使用ACTION_OPEN_DOCUMENT触发画廊)来尝试此操作:如果从“最近”select了一个图像,它将起作用; 如果您从“下载”中select图片,则会中断。

那么如何?

直接的答案是你不这样做 ,你不能在新版本的操作系统中find来自内容uris的文件path。 可以说,并不是所有的内容uris都指向图片甚至文件。

这对我来说是完全合适的,起初我曾努力避免这种情况。 但是, 如果我们不应该使用path我们应该如何使用ExifInterface类?

我不明白现代应用程序如何做到这一点 – find方向和元数据是你立即面临的问题, ContentResolver不提供任何API在这个意义上。 你有ContentResolver.openFileDescriptor()和类似的东西,但没有读取元数据(真正在该文件中)的API。 可能有外部库从stream中读取Exif东西,但我想知道通用/平台的方式来解决这个问题。

我在谷歌的开源应用程序中search了类似的代码,但没有发现任何东西。

Solutions Collecting From Web of "有关如何从URI获取Exif数据的最终答案"

以下内容适用于我testing过的所有应用程序,无论如何:

这只有在Uri碰巧来自MediaStore时才有效。 如果Uri恰好来自其他任何东西,它将会失败。

直接的答案是你不这样做,你不能在新版本的操作系统中find来自内容uris的文件path。 可以说,并不是所有的内容uris都指向图片甚至文件。

正确。 我曾多次指出过这个问题,比如这里 。

我们应该如何使用ExifInterface类,如果我们不应该使用path?

你没有。 使用其他代码来获取EXIF标题。

可能有外部库从stream中读取Exif的东西,但我想知道通用/平台的方式来解决这个问题。

使用外部库。

我在谷歌的开源应用程序中search了类似的代码,但没有发现任何东西。

你会发现一些在Mms应用程序 。

用一些示例代码展开alex.dorokhov的答案。 支持库是一个伟大的路要走。

的build.gradle

 dependencies { ... compile "com.android.support:exifinterface:25.0.1" ... } 

示例代码:

 import android.support.media.ExifInterface; ... try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) { ExifInterface exif = new ExifInterface(inputStream); int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); } catch (IOException e) { e.printStackTrace(); } 

之所以我必须这样做,一旦我们开始瞄准api 25(也许在24 +也可能是一个问题),但仍支持回api 19,在Android 7我们的应用程序将崩溃,如果我传递一个URI到相机是只是引用一个文件。 因此,我不得不创build一个URI传递给像这样的相机意图。

 FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileprovider", tempFile); 

问题是那个文件不可能把URI变成一个真正的文件path(除了保存到临时文件path)。

从内容URI(实际上是一个InputStream)获取EXIF现在在支持库中可用。 请参阅: https : //android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html