暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

如何把图片存在HarmonyOS系统相册?

鸿蒙技术社区 2022-03-01
2939

前几天有个同事问我 HarmonyOS 中如何把图片存在系统相册,当时我就懵逼了,鸿蒙的好像真的不怎么懂?而且这个操作在我们平时开发时也经常用到,所以搞起。


效果展示:

踩坑之路


应该官网有介绍吧,去官网看看,发现是有一丢丢介绍,附上链接:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-data-mgmt-storage-0000001050994909


都说安卓和鸿蒙差不多,应该思路是差不多的吧,于是找到一篇文章:
https://www.jianshu.com/p/db3dd7ad92f4


里面有 MediaStore 类,用于操作系统媒体数据库的类,鸿蒙确实也有个类似的类 AVStorage,但是现在开放的功能不如 MediaStore 强大。


后面发现是鸿蒙的设计思路有点像 iOS 的,每个应用的都有独自沙河目录,每个 App 的数据都存储在当前的应用当中,这样大大的确保数据的隐蔽性和安全性,这样比安卓安全性好很多。


保存图片到系统相册


demo 布局:
//展示图片
<Image
    ohos:id="$+id:show_photo"
    ohos:height="200fp"
    ohos:width="200fp"
    ohos:image_src="$media:empty"
    ohos:scale_mode="zoom_center"
    ohos:top_margin="30fp"
    />

 //选择图片     
<Text
    ohos:id="$+id:select_photo"
    ohos:height="match_content"
    ohos:width="match_content"
    ohos:background_element="$graphic:background_ability_main"
    ohos:layout_alignment="horizontal_center"
    ohos:text="选择图片"
    ohos:text_size="20vp"
    ohos:top_margin="10fp"
    />

  //保存图片
<Text
    ohos:top_margin="10fp"
    ohos:id="$+id:save_photo"
    ohos:height="match_content"
    ohos:width="match_content"
    ohos:background_element="$graphic:background_ability_main"
    ohos:layout_alignment="horizontal_center"
    ohos:text="保存图片"
    ohos:text_size="20vp"
    />


效果如图:

涉及权限


config.json 权限配置如下:

"reqPermissions": [
  {"name""ohos.permission.READ_USER_STORAGE"},
  {"name""ohos.permission.WRITE_USER_STORAGE"}
]


动态申请权限


需要动态申请这两个权限,申请时会有权限弹窗,不写的话,不会有权限弹窗,但是也是可以使用的。

String[] permissions = {"ohos.permission.READ_USER_STORAGE""ohos.permission.WRITE_USER_STORAGE"};
requestPermissionsFromUser(permissions, 0);


保存图片


获取到权限之后,就可以保存图片到系统相册了,我们媒体的增删改查都需要用到 DataAbilityHelper 和 AVStorage。

 //保存图片到相册 fileName文件名  PixelMap 图片数据
private void saveImageToLibrary(String fileName, PixelMap pixelMap) {
    try {
        ValuesBucket valuesBucket = new ValuesBucket();
        //文件名
        valuesBucket.putString(AVStorage.Images.Media.DISPLAY_NAME, fileName);
       //相对路径
        valuesBucket.putString("relative_path""DCIM/");
       //文件格式,类型要一定要注意要是JPEG,PNG类型不支持
        valuesBucket.putString(AVStorage.Images.Media.MIME_TYPE, "image/JPEG");
        //应用独占:is_pending设置为1时表示只有该应用能访问此图片,其他应用无法发现该图片,当图片处理操作完成后再吧is_pending设置为0,解除独占,让其他应用可见
        valuesBucket.putInteger("is_pending"1);

      //鸿蒙的helper.insert方法和安卓的contentResolver.insert方法有所不同,安卓方法直接返回一个uri,我们就可以拿来直接操作,而鸿蒙方法返回官方描述是Returns the index of the inserted data record(返回插入的数据记录的索引),这个index我的理解就是id,因此,我们需要自己在后面拼出文件的uri再进行操作
        DataAbilityHelper helper = DataAbilityHelper.creator(this);
        int index = helper.insert(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, valuesBucket);
        Uri uri = Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(index));

        //获取到uri后,安卓通过contentResolver.openOutputStream(uri)就能获取到输出流来写文件,而鸿蒙没有提供这样的方法,我们就只能通过uri获取FileDescriptor,再通过FileDescriptor生成输出流打包编码成新的图片文件,这里helper.openFile方法一定要有“w”写模式,不然会报FileNotFound的错误。
        FileDescriptor fd = helper.openFile(uri, "w");
        ImagePacker imagePacker = ImagePacker.create();
        ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
        OutputStream outputStream = new FileOutputStream(fd);
        packingOptions.format = "image/jpeg";
        packingOptions.quality = 90;
        boolean result = imagePacker.initializePacking(outputStream, packingOptions);
        if (result) {
            result = imagePacker.addImage(pixelMap);
            if (result) {
                long dataSize = imagePacker.finalizePacking();
            }
        }
        outputStream.flush();
        outputStream.close();
        valuesBucket.clear();
        //解除独占
        valuesBucket.putInteger("is_pending"0);
        helper.update(uri, valuesBucket, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
}


效果如下:

读取本地相册图片


在 config.json 中配置读取文件权限:
ohos.permission.READ_USER_STORAGE


"reqPermissions": [{"name""ohos.permission.READ_USER_STORAGE"}]


在 ability 中手动申请权限:

String[] permissions = {"ohos.permission.READ_USER_STORAGE"};
requestPermissionsFromUser(permissions, 0);


弹出数据来源选择框,获取数据来源的方式。

//选择图片
 private void selectPhoto() {
        //调起系统的选择来源数据视图
        Intent intent = new Intent();
        Operation opt=new Intent.OperationBuilder().withAction("android.intent.action.GET_CONTENT").build();
        intent.setOperation(opt);
        intent.addFlags(Intent.FLAG_NOT_OHOS_COMPONENT);
        intent.setType("image/*");
        startAbilityForResult(intent, imgRequestCode);
    }


效果如图:

下面是选择图片的回调,imgRequestCode 字段的是自定义的,必须是 int 的类型。


这个字段是和上面的 selectPhoto() 方法里面的 imgRequestCode 是一致的,根据这个 imgRequestCode 来判断是否从选择图片的回调回来的。

/*选择图片回调*/
@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
    if(requestCode==imgRequestCode && resultData!=null)
    {
        //选择的Img对应的Uri
        String chooseImgUrl=resultData.getUriString();
        //定义数据能力帮助对象
        DataAbilityHelper helper=DataAbilityHelper.creator(getContext());
        //定义图片来源对象
        ImageSource imageSource = null;
        //获取选择的Img对应的Id
        String chooseImgId=null;
        //如果是选择文件则getUriString结果为dataability:///com.android.providers.media.documents/document/image%3A437,其中%3A437是":"的URL编码结果,后面的数字就是image对应的Id
        //如果选择的是图库则getUriString结果为dataability:///media/external/images/media/262,最后就是image对应的Id
        //这里需要判断是选择了文件还是图库
        if(chooseImgUri.lastIndexOf("%3A")!=-1){
            chooseImgId = chooseImgUri.substring(chooseImgUri.lastIndexOf("%3A")+3);
        }
        else {
            chooseImgId = chooseImgUri.substring(chooseImgUri.lastIndexOf('/')+1);
        }
        //获取图片对应的uri,由于获取到的前缀是content,我们替换成对应的dataability前缀
        Uri uri=Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,chooseImgId);
        try {
            //读取图片
            FileDescriptor fd = helper.openFile(uri, "r");
            imageSource = ImageSource.create(fd, null);
            //创建位图
            PixelMap pixelMap = imageSource.createPixelmap(null);
            //设置图片控件对应的位图
            photo.setPixelMap(pixelMap);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (imageSource != null) {
                imageSource.release();
            }
        }
    }
}


总结


官网现有文档不多,开发鸿蒙的时候遇到很多问题,有安卓基础的小伙伴可以参考安卓的思路去解决,应该可以事半功倍,希望本次分享对大家有所帮助。


作者:庄茂裕

有奖问卷,扫码参加


👇点击关注鸿蒙技术社区👇
了解鸿蒙一手资讯

求分享

求点赞

求在看

文章转载自鸿蒙技术社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论