使用新的 Android 范围存储将 exif 信息从一个 URI 复制到另一个?

Copy exif info from one Uri to the other with the new Android Scoped Storage?

我有两个 Uri 来自不同的文件,其模式“内容”由 Android SAF API 获得,我需要将 exif 信息从一个文件复制到另一个文件。目标文件是由我的应用程序创建的,因此我已授予对它的写入权限。

通过 ContentResolver 我可以获得一个 InputStream,我可以用它来实例化一个 ExifInterface,从中我可以从源 Uri 中提取 exif:

InputStream ins = context.getContentResolver().openInputStream(imageInputUri);
ExifInterface originalExif = new ExifInterface(ins);

到目前为止一切顺利...
问题是将 exif 信息写入目标 Uri。我不能像对源代码那样做,因为用 InputStream 创建的 ExifInferface 是只读的,不能用于将 exif 信息保存为 stated in the docs

我怎样才能解决这个问题并最终能够将这些 exif 信息从一个文件复制到另一个文件?

由于没有人给出答案,但感谢@blackapps 的评论,我能够自己找到解决方案。如果其他人找到这个 post 搜索同一问题的答案,我会 post 在这里。

事实证明,ExifInterface 有一个构造函数接收 FileDescriptor 作为参数,并且可以通过使用从 Uri 打开 FileDescriptor ContentResolver。那么解决方案很简单,为了成功打开 FileDescriptor 唯一需要额外注意的是使用“rw”模式,因为 ExifInterface 需要读取和写入信息而不仅仅是写入.

示例代码如下:

InputStream ins = null;
ParcelFileDescriptor outFd = null;
try  {
    ins = context.getContentResolver().openInputStream(imageInputUri);
    ExifInterface originalExif = new ExifInterface(ins);

    outFd = context.getContentResolver().openFileDescriptor(imageOutputUri, "rw");
    ExifInterface newExif = new ExifInterface(outFd.getFileDescriptor());
    copyExifAttributes(originalExif, newExif, width, height);

} catch (IOException e) {
    Log.d(TAG, e.getMessage(), e);
} finally {
    if (ins != null) {
        try {
            ins.close();
        } catch (IOException e) {
            Log.d(TAG, e.getMessage(), e);
        }
    }
    if (outFd != null) {
        try {
            outFd.close();
        } catch (IOException e) {
            Log.d(TAG, e.getMessage(), e);
        }
    }
}

请注意,此代码不适用于 Android 早于 Lollipop (api 21) 的版本。这是一个 ExifInterface 限制,因为它需要一个可搜索的 FileDescriptor 才能工作,并且 ExifiInterface 使用 native call for doing this. Looking at the Android docs 可以看到此本机代码仅在 Lollipop 之后可用。