Android 10开发之 保存、读取图片

Android 10开发之 保存、读取图片
概述

从Android 10(Q)开始,谷歌就开始修改了外部存储权限,叫做分区存储,分区存储可以分为两个目录,分别是 沙盒目录(App-specific directory 和 公共目录(Public Directory)
沙盒目录

沙盒目录存储在 /Android/data/包名,保存文件到该目录,一般通过 Context.getExternalFilesDir() ,例如:context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) 表示路径为:/Android/data/包名/Pictures/,app一旦卸载,沙盒目录下的文件都会被删除。Android 10以上去除了WRITE_EXTERNAL_STORAGE权限,不需要这个权限就可以保存文件到沙盒目录
在这里插入图片描述公共目录

公共目录包括 多媒体目录 和 下载目录

公共目录的媒体文件(Photos, Images, Videos, Audio)通过MediaStore来访问,另外,MediaStore的DATA字段从Android 10开始被标记为deprecated,通过该字段获取的文件路径不再可靠,Android 10以上新增字段RELATIVE_PATH,代表文件的相对路径,在使用MediaStore保存媒体文件时,可以通过设置该字段来设置媒体文件保存的文件夹
保存文件(以图片为例)
沙盒目录

/**
     * 保存图片到沙盒目录
     * @param context 上下文
     * @param fileName 文件名
     * @param bitmap 文件
     * @return 路径,为空时表示保存失败
     */
    public static String FileSaveToInside(Context context, String fileName, Bitmap bitmap) {
        FileOutputStream fos = null;
        String path = null;
        try {
            //设置路径 /Android/data/com.panyko.filesave/Pictures/
            File folder = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            //判断目录是否存在
            //目录不存在时自动创建
            if (folder.exists() ||folder.mkdir()) {
                File file = new File(folder, fileName);
                fos = new FileOutputStream(file);
                //写入文件
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                fos.flush();
                path = file.getAbsolutePath();
            }
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            try {
                if (fos != null) {
                    //关闭流
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        //返回路径
        return path;
    }

保存文件到沙盒目录时,操作简单,不需要判断Android版本做兼容

公共目录
/**
     * 保存文件到公共目录
     * @param context 上下文
     * @param fileName 文件名
     * @param bitmap 文件
     * @return 路径,为空时表示保存失败
     */
    public static String fileSaveToPublic(Context context, String fileName, Bitmap bitmap) {
        String path = null;

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            //Android 10以下版本
            FileOutputStream fos = null;
            try {
                //设置路径 Pictures/
                File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
                //判断目录是否存在
                //目录不存在时自动创建
                if (folder.exists() || folder.mkdir()) {
                    File file = new File(folder, fileName);
                    fos = new FileOutputStream(file);
                    //写入文件
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
                    fos.flush();
                    path = file.getAbsolutePath();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            //Android 10及以上版本
            
            //设置路径 Pictures/
            String folder = Environment.DIRECTORY_PICTURES;
            //设置保存参数到ContentValues中
            ContentValues values = new ContentValues();
            //设置图片名称
            values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
            //设置图片格式
            values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
            //设置图片路径
            values.put(MediaStore.Images.Media.RELATIVE_PATH, folder);
            //执行insert操作,向系统文件夹中添加文件
            //EXTERNAL_CONTENT_URI代表外部存储器,该值不变
            Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            OutputStream os = null;
            try {
                if (uri != null) {
                    //若生成了uri,则表示该文件添加成功
                    //使用流将内容写入该uri中即可
                    os = context.getContentResolver().openOutputStream(uri);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
                    os.flush();
                    path = uri.getPath();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return path;
    }

Android10开始,访问公共目录的方式有改变,通过 MediaStore 来访问,因为不能完全保证Android10以下的手机能通过MediaStore方式保存文件,因此在保存文件时,根据Android版本分成两种情况(Android10以下、Android10及以上),这样稳妥

读取文件(以图片为例)
/**
     * 根据路径和名字查出文件
     * @param context 上下文
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    public static Uri FileGetFromPublic(Context context, String filePath, String fileName) {
        String queryPath;
        //判断是否有加斜杠
        if (!filePath.endsWith("/")) {
            filePath = filePath + File.separator;
        }
        //判断Android版本
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            //Android10以下
            //加上文件名
            filePath = filePath + fileName;
            //使用DATA字段做查询
            queryPath = MediaStore.Images.Media.DATA;
        } else {
            //Android10及以上
            //使用RELATIVE_PATH字段做查询
            queryPath = MediaStore.Images.Media.RELATIVE_PATH;
        }
        //拼接查询条件
        //queryPath表示文件所在路径
        //DISPLAY_NAME表示文件名
        String selection = queryPath + "=? and " + MediaStore.Images.Media.DISPLAY_NAME + "=?";
        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Files.FileColumns._ID}, selection, new String[]{filePath, fileName}, null);

        if (cursor != null && cursor.moveToFirst()) {
            //查出id
            int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));
            //根据id查询URI
            Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
            return uri;
        }
        //关闭查询
        if (cursor != null) {
            cursor.close();
        }
        return null;
    }

Android 10以下是可以通过 MediaStore.Images.Media.DATA 获取文件绝对路径,Android 10 及以上DATA字段被弃用,增加了 MediaStore.Images.Media.RELATIVE_PATH获取文件相对路径。这里根据版本判断,当Android10以下时,查询DATA字段;当Android10及以上时,查询RELATIVE_PATH字段,再获取id,根据id获取URI

这里要注意下,如果是Android10及以上的,通过RELATIVE_PATH查询时,路径后面一定要检查是否加了斜杠,如果没加,一定要加斜杠;如果是Android10以下的,通过DATA查询时,路径一定要完整(包括文件名),我就是因为这个原因,一直查不到数据,后面把media数据库导出来,查看了一下才知道。
Android9,data字段:在这里插入图片描述
Android11,relative_path字段:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值