【问题标题】:How to have the Camera Intent put a photo into internal storage?如何让相机意图将照片放入内部存储?
【发布时间】:2013-08-02 19:39:23
【问题描述】:

我整天都在做这件事,但似乎无法让它发挥作用。根据以前的工作人员所说,它以前可以工作。

    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);

类似的代码已在此处发布,但它似乎不适用于我在 4.2.2 上的 nexus 4。我尝试了外部存储,然后它工作正常。任何关于为什么它可能不起作用的见解都会非常有帮助。谢谢。

【问题讨论】:

    标签: android storage photos


    【解决方案1】:

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

    【讨论】:

    • 看,这正是我一直在和我的老板争论的。但不幸的是,因为上面的代码在某些时候有效,他们希望以这种方式保留它。我确实有一个问题想澄清:Context.MODE_WORLD_WRITEABLE。我假设它允许任何应用程序从那里访问文件和内容,因为它不是MODE_PRIVATE。它不是这样工作的吗?基本上是因为它在某些时候有效,我猜这就是它的作用。
    • @Andy:“但不幸的是,因为上面的代码在某些时候工作,他们希望以这种方式保持它”——如果有的话,它肯定不会可靠地工作。 “这样不行吗?” ——是和不是。是的,该文件是可写的。您假设相机应用程序正在写入现有文件。它可能会尝试删除现有文件并创建一个新文件。如果 目录 是全局可写的,它们可能会成功删除,但随后您将无法读取生成的文件,因为它属于其他应用程序并且可能是私有的。
    • @Andy:请记住,有成千上万的相机应用程序,其中任何一个都可以响应您的Intent,因此行为会有所不同。仅仅因为你可以让一个应用程序运行并不意味着所有应用程序都会。
    【解决方案2】:

    我正在与同样的问题作斗争,并在提出问题后很久才得到解决方案,但我相信这可能对社区有用。

    随着 Android 6.0 / API 级别 23 中引入的新 dynamic permissions,主题问题变得尤为重要,因为您需要在运行时请求权限并处理用户的接受和拒绝反应。要使用相机活动,您需要先请求相应的权限(android.permission.CAMERA)。那么,如果你将图片存储在外部目录中,那么用户也需要给你的应用授予相应的权限android.permission.READ_EXTERNAL_STORAGE。在用户即将执行预期操作时(例如,如果在按下“拍照”按钮后立即出现相机访问权限请求),运行时权限请求对用户来说似乎很自然。但是,如果您使用外部存储来保存相机图片,则需要在您的应用拍照时同时要求两个权限:(1)使用相机和(2)访问外部存储。后者可能会令人沮丧,因为不一定清楚为什么您的应用会尝试访问用户文件,而用户只希望拍摄一张照片。

    解决方案允许避免外部存储并直接保存相机图片包括使用content providers。根据storage options documentation

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

    这正是您需要允许相机活动将图片直接保存到应用程序的本地存储中,这样您就可以轻松访问它而无需请求其他权限(仅需要授予相机访问权限) .

    提供了一篇带有代码示例的好文章here。以下受本文启发的通用代码在我们的应用程序中使用。

    内容提供者类:

    /**
     * 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” - 无法添加到清单中。所以我只能相信它必须通过要求相机应用程序写入我的应用程序的内部存储来做一些事情......有什么想法吗?
    • 这是一个更好的解决方案。
    【解决方案3】:

    我遇到了同样的问题。我通过首先将照片保存在外部存储器上然后复制到内部存储器来解决它。希望这会有所帮助。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多