如何让相机意图将照片存储到内部存储器中?

3

我一整天都在做这个,但似乎无法使其正常工作。之前的开发人员说过它以前是可以工作的。

    cameraIntent = new Intent("android.media.action.IMAGE_CAPTURE");
        String imageName = CustomApp.getTimeStamp(0) ;
        String path = CustomApp.getCurrentActivity().getDir("images", Context.MODE_WORLD_WRITEABLE).getPath()+File.separator+imageName;

        File file = new File(path) ;
        Uri img = Uri.fromFile(file) ;

        Intent passthruDataIntent = new Intent();

        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, img);
        CustomApp.getCurrentActivity().startActivityForResult(cameraIntent, CustomConstants.REQUESTCODE_CAMERA);

类似的代码已经发布在这里,但是它似乎在我Nexus 4上的4.2.2版本上不起作用。我尝试使用外部存储器,然后它就可以正常工作了。任何关于为什么它可能不起作用的见解都将非常有帮助。谢谢。

3个回答

8

内部存储对每个应用程序都是私有的--第三方相机应用程序无权写入您的内部存储。


看,这正是我一直在向老板辩论的。但不幸的是,因为上面的代码曾经起作用,他们想保持它的方式。我有一个问题需要澄清:Context.MODE_WORLD_WRITEABLE。我做了一个假设,认为它允许任何应用程序从那里访问文件和其他东西,因为它不是MODE_PRIVATE。它不是这样工作的吗?基本上因为它曾经起过作用,我猜想它就是这样做的。 - Andy
1
@Andy:“但不幸的是,因为上面的代码曾经起作用,他们想保持这种方式” - 如果有的话,它肯定不会可靠地工作。 “这样行不行?” - 是的和不是的。是的,文件是可写的。您假设相机应用程序正在写入现有文件。它可能尝试删除现有文件并创建新文件。如果目录是全局可写的,则它们可能会成功删除,但然后您将无法读取生成的文件,因为它将由其他应用程序拥有并且可能是私有的。 - CommonsWare
2
@Andy:请记住,有成千上万的相机应用程序,其中任何一个都可以响应您的“Intent”,因此行为会有所不同。仅仅因为您可以让一个应用程序工作,并不意味着所有应用程序都能够工作。 - CommonsWare

5
我曾经也遇到过同样的问题,但是我在问题被提出后很长一段时间才找到解决办法,但我相信这可能对社区有用。
随着 Android 6.0 / API 级别 23 引入的新的动态权限,话题问题变得特别重要,因为您需要在运行时请求权限并处理用户的接受和拒绝反应。如果要使用相机活动,则需要首先请求相应的权限(android.permission.CAMERA)。然后,如果将图片存储在外部目录中,则还需要获得用户授予的相应的权限 android.permission.READ_EXTERNAL_STORAGE。当用户即将执行所需操作时(例如,如果相机访问权限请求出现在按下“拍照”按钮后),运行时权限请求对用户来说似乎是自然的。但是,如果您使用外部存储器保存相机图像,则在应用程序拍照时需要同时请求两个权限:(1)使用相机和(2)访问外部存储器。后者可能令人沮丧,因为不一定清楚为什么您的应用程序试图访问用户文件,而用户只期望拍摄照片。 解决方案,可以避免使用外部存储并直接保存相机图片的方法是使用内容提供者。根据存储选项文档

Android为您提供了一种将甚至您的私有数据暴露给其他应用程序的方法——使用内容提供者。内容提供者是一个可选组件,它公开了对您的应用程序数据的读/写访问权限,受到您想要强制执行的任何限制。

这正是您需要允许相机活动直接将图片保存到您应用程序的本地存储中,以便您稍后可以轻松访问它,而无需请求其他权限(只需要授予相机访问权限)。
一个很好的文章和代码示例在这里提供。我们应用程序中使用以下通用代码来完成此操作。
内容提供者类:
/**
 * A content provider that allows to store the camera image internally without requesting the
 * permission to access the external storage to take shots.
 */
public class CameraPictureProvider extends ContentProvider {
    private static final String FILENAME = "picture.jpg";

    private static final Uri CONTENT_URI = Uri.parse("content://xyz.example.app/cameraPicture");

    @Override
    public boolean onCreate() {
        try {
            File picture = new File(getContext().getFilesDir(), FILENAME);
            if (!picture.exists())
                if (picture.createNewFile()) {
                    getContext().getContentResolver().notifyChange(CONTENT_URI, null);
                    return true;
                }
        } catch (IOException | NullPointerException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Nullable
    @Override
    public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
        try {
            File picture = new File(getContext().getFilesDir(), FILENAME);
            if (!picture.exists())
                picture.createNewFile();
            return ParcelFileDescriptor.open(picture, ParcelFileDescriptor.MODE_READ_WRITE);
        } catch (IOException | NullPointerException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        String lc = uri.getPath().toLowerCase();
        if (lc.endsWith(".jpg") || lc.endsWith(".jpeg"))
            return "image/jpeg";
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

需要在应用程序清单中声明内容提供程序:

    <provider android:authorities="xyz.example.app"
        android:enabled="true"
        android:exported="true"
        android:name="xyz.example.app.CameraPictureProvider" />

最后,为了使用内容提供程序来捕获相机图像,需要从调用活动中调用以下代码:

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, CameraPictureProvider.CONTENT_URI);
startActivityForResult(takePictureIntent, 0);

请注意,相机权限请求需要单独处理(在提供的代码示例中未执行)。
值得注意的是,仅当您使用版本号为23或更高的构建工具时,才需要处理权限请求。相同的代码与较低级别的构建工具兼容,并且在您不受运行时权限请求干扰但仅想避免使用外部存储时非常有用。

你好,我完全理解你在这篇文章中没有处理权限的问题。但是,你能否提及一些相关内容呢?例如 - 我遇到了“Permission Denial: getCurrentUser() ... requires android.permission.INTERACT_ACROSS_USERS”的问题 - 这无法在清单文件中添加。因此,我认为这可能与请求相机应用程序将数据写入我的应用程序的内部存储有关... 你有什么想法吗? - marienke
这是一个更好的解决方案。 - YellowJ

1

我遇到了同样的问题。我通过先将照片保存在外部存储器上,然后再复制到内部存储器上来解决了这个问题。希望这可以帮到你。


如果SD卡不可用,请参见https://dev59.com/02sz5IYBdhLWcg3wsaAN。 - Anti Earth

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