如何获取从FileProvider类拍摄的图像文件的方向?

4

背景

针对 API 24 或以上版本,开发者需要使用 FileProvider(或自己的 ContentProvider)来代替简单的 "Uri.fromFile" 命令,以便让其他应用程序访问该应用程序的文件。

问题

我尝试打开一个相机应用程序,让它将文件保存到我的应用程序的私有存储中,使用以下代码:

    final Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    final File photoFile = FileHelper.generateTempCameraFile(this);
    mCameraCachedImageURI = FileProvider.getUriForFile(this, getPackageName()+ ".provider", photoFile);
    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCameraCachedImageURI);
    takePictureIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    startActivityForResult(takePictureIntent, REQ_CODE_PICK_IMAGE_FROM_CAMERA);

provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>

清单文件
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

这个开始还好,相机应用程序被启动了,但在返回的结果中,我无法使用之前正常工作的代码获取图像文件的方向:

@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    int orientation = getOrientation(this,mCameraCachedImageURI );
    ...

public static int getOrientation(final Context context, final Uri photoUri) {
    Cursor cursor = null;
    try {
        cursor = context.getContentResolver().query(photoUri,
                new String[]{MediaStore.Images.ImageColumns.ORIENTATION}, null, null, null);
    } catch (final Exception ignored) {
    }
    if (cursor == null) {
        // fallback
        return getOrientation(photoUri.getPath());
    }
    int result = 0;
    if (cursor.getCount() > 0) {
        cursor.moveToFirst();
        result = cursor.getInt(0);
    }
    cursor.close();
    return result;
}

public static int getOrientation(final String filePath) {
    if (TextUtils.isEmpty(filePath))
        return 0;
    try {
        final ExifInterface exifInterface = new ExifInterface(filePath);
        final int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);
        int rotate = 0;
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_270:
                rotate = 270;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                rotate = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_90:
                rotate = 90;
                break;
        }
        return rotate;
    } catch (final IOException ignored) {
    }
    return 0;
}

这段代码会崩溃,因为光标在运行此代码时没有任何列。
不仅如此,即使将代码更改为使用回退函数,也无法工作,因为它会向文件路径添加一个附加部分(在本例中为“external”)。
我发现的问题是,这里的contentResolver使用了FileProvider,而不是之前版本的Android所使用的方法。
问题在于,我需要此URI的ContentProvider才能授予相机应用程序访问此文件的权限...
问题是:如何使用新的FileProvider Uri获取上述代码(或者更好的代码)中的方向?也许我可以强制ContentResolver使用以前的ContentProvider来获取光标所需的列数据?
我真的必须在这里创建自己的ContentProvider吗?

为什么不使用 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 或者 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?更多信息请参考此处 - pskink
我不想将公共文件放在图库应用程序中也可以访问的位置。这是一个临时文件,应该在应用程序本身中使用,并且很快就会被删除。 - android developer
如果数据是私有的(其他应用程序无法访问),那么你为什么需要FileProvider?只为了MediaStore.ACTION_IMAGE_CAPTURE吗? - pskink
@pskink 这个私密性足够强,以至于用户不应该轻易找到并查看它。我只需要拍摄一张照片,然后用它做些事情。 - android developer
1个回答

0

Uri不是文件。您正在调用getPath()在一个Uri上,并将其视为文件路径。只有当Uri中的方案为file时,才起作用。在您的情况下,它不是。

你已经有一个指向该文件的File对象。它被称为photoFile。将其保存在您的活动字段中,并在保存的实例状态Bundle中保存它(因为您的进程可能在相机应用程序处于前台时被终止)。然后,使用photoFile。但不要使用内置的ExifInterface,因为它存在安全漏洞

如何使用上述代码(或更好的代码)使用新的FileProvider Uri获取方向?

你不需要。使用photoFile

也许我可以强制ContentResolver使用先前的ContentProvider来获取游标所需列的数据?
我不知道为什么你会期望接受一个Uri参数的getOrientation()方法能够工作。你正在查询MediaStore关于一个不来自MediaStore的Uri的数据。
我需要在这里创建自己的ContentProvider吗?
不需要,因为FileProvider不是你的问题。你将在自己的ContentProvider中遇到相同有缺陷的getOrientation()方法。

如果不使用uri或Exif,我该使用什么来获取捕获的图像文件的方向?我之前只使用了ExifInterface作为备选方案,但现在我不知道该使用什么替代品了。 - android developer
@androiddeveloper:“我应该使用什么来获取捕获的图像文件的方向?”——随机数生成器。说真的,如果你不查看JPEG EXIF头来获取方向,就没有办法知道方向。MediaStore检查EXIF头以查找方向,并将其缓存在自己的数据库中,用于索引的图像。 - CommonsWare
我该如何获取方向呢?我应该调用什么函数?有文件路径和从FileProvider创建的Uri。使用MediaStore在uri上没有起作用,无论是来自FileProvider还是“Uri.fromFile”。 - android developer
@androiddeveloper:调用 getOrientation(final String filePath)。这个函数已经在你的代码中了。然而,filePath 的值不是 UrigetPath() ,而是 photoFile.getAbsolutePath()。我建议换用另一个 ExifInterface 实现,因为 Android SDK 中的这个存在安全漏洞 - CommonsWare
1
@androiddeveloper:Android 7.0+设备应该打补丁。Google为6.0(可能还包括5.x,我忘了)提供了一个补丁。然而,由于操作系统升级的记录不佳,许多5.x/6.x设备很少会看到这个补丁。如果您的minSdkVersion是24或更高版本,则使用SDK的ExifInterface应该没问题;否则我会使用其他东西。 - CommonsWare
显示剩余9条评论

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