37 相册读取与图片选择PhotoViewPicker 实战前言图37 相册读取与图片选择PhotoViewPicker 实战 运行效果截图HarmonyOS NEXT除了直接拍照从相册选择已有图片也是常见的图片获取方式。鸿蒙提供了PhotoViewPicker——一个系统级的图片选择器让用户可以从相册中选择一张或多张图片。本文以鹿鹿·笔迹心理分析项目中 [CapturePage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/CapturePage.ets) 的pickFromAlbum()方法为例深入解析 PhotoViewPicker 的使用方法和相册选择流程。鸿蒙官方·PhotoViewPicker 文档developer.huawei.com项目源码仓库harmony-app GitHub图PhotoViewPicker 相册选择流程——创建选择器 → 配置选项 → 获取 URI文件系统系统相册 UIPhotoViewPickerCapturePage文件系统系统相册 UIPhotoViewPickerCapturePagenew photoAccessHelper.PhotoViewPicker()select(PhotoSelectOptions)打开系统相册界面用户浏览并选择图片返回选中图片PhotoSelectResult { photoUris }通过 URI 读取图片数据一、PhotoViewPicker 基础1.1 模块导入import{photoAccessHelper}fromkit.MediaLibraryKit1.2 基本使用privateasyncpickFromAlbum(){try{// ① 创建选择器constphotoPickernewphotoAccessHelper.PhotoViewPicker()// ② 配置选项constpickerOptions:photoAccessHelper.PhotoSelectOptions{maxSelectNumber:1// 最多选择 1 张}// ③ 打开选择器constresult:photoAccessHelper.PhotoSelectResultawaitphotoPicker.select(pickerOptions)// ④ 处理结果if(resultresult.photoUrisresult.photoUris.length0){this.previewPathresult.photoUris[0]this.hasPreviewtruethis.sourcegallery}}catch(error){hilog.error(0x0000,TAG,相册选择失败: %s,JSON.stringify(error))}}二、PhotoSelectOptions 配置2.1 选项详解interfacePhotoSelectOptions{maxSelectNumber:number// 最大选择数量MIME?:string[]// 文件类型过滤isEdit?:boolean// 是否允许编辑}2.2 实际配置参数本项目值说明maxSelectNumber1一次分析一张笔迹图片MIME未指定默认所有图片类型isEdit未指定选择后不编辑2.3 扩展配置示例// 多选 MIME 过滤constpickerOptions:photoAccessHelper.PhotoSelectOptions{maxSelectNumber:9,MIME:[image/jpeg,image/png],}三、PhotoSelectResult 结果处理3.1 返回结构interfacePhotoSelectResult{photoUris:string[]// 所选图片的 URI 列表}3.2 URI 的使用result.photoUris[0]// 返回类似content://media/external/images/media/12345// 或/data/storage/el2/base/files/...// 这个 URI 可以直接用于// 1. image.createImageSource(uri) — 解码图像// 2. Image(uri) — ArkUI 图片组件展示// 3. fs.openSync(uri) — 文件操作四、拍照/相册双模式切换4.1 模式切换策略CapturePage 支持两种来源——拍照和相册通过source参数控制aboutToAppear(){// 从路由参数读取来源constparamsthis.getUIContext().getRouter().getParams()asRecordstring,Objectif(paramstypeofparams[source]string){this.sourceparams[source]asstring}if(this.sourcegallery){this.pickFromAlbum()// → 相册模式}else{this.initCamera()// → 相机模式}}4.2 触发来源// 跳转到 CapturePage — 拍照模式this.getUIContext().getRouter().pushUrl({url:pages/CapturePage,params:{source:camera}})// 跳转到 CapturePage — 相册模式this.getUIContext().getRouter().pushUrl({url:pages/CapturePage,params:{source:gallery}})4.3 UI 中的相册入口在拍照页面底部用户也可以点击相册按钮随时切换到相册选择// 拍照模式底栏中的相册按钮Text().fontSize(22).fontColor(rgba(255,255,255,0.8)).onClick(()this.pickFromAlbum())五、MediaLibraryKit 的其他能力5.1 获取相册列表asyncfunctiongetAlbums():PromisephotoAccessHelper.Album[]{consthelperphotoAccessHelper.getPhotoAccessHelper(getContext(this))constalbumsawaithelper.getAlbums(photoAccessHelper.AlbumType.USER,photoAccessHelper.AlbumSubtype.USER_GENERIC)returnalbums}5.2 保存图片到相册import{photoAccessHelper}fromkit.MediaLibraryKitasyncfunctionsaveToAlbum(uri:string):Promisevoid{consthelperphotoAccessHelper.getPhotoAccessHelper(getContext(this))// 将文件添加到系统相册awaithelper.addAsset(uri)}六、PhotoViewPicker vs 自定义相册对比PhotoViewPicker项目使用自定义相册实现复杂度低3 行代码高权限 查询 UIUI 一致性系统原生相册自定义 UI多选支持✅ 内置需要自行实现权限要求自动处理需要 MANAGE_MEDIA推荐场景快速集成需定制 UI 的场景七、路径与 URI 的处理7.1 路径统一拍照和相册选择的图片路径格式不同但后续处理方式一致// 拍照文件系统路径this.previewPath/data/storage/el2/base/cache/capture_${Date.now()}.jpg// 相册content:// URIthis.previewPathresult.photoUris[0]// content://media/...// 两种路径传入 AnalyzingPage 后的使用constimagePathAppStorage.getstring(capture_image_path)??awaitHandwritingDao.create({image_path:imagePath,// 原样保存路径// ...})7.2 路径格式统一化privatenormalizePath(uri:string):string{if(uri.startsWith(content://)){// content:// URI → 复制到应用缓存目录// 这需要读取文件内容并写入临时目录returnuri// 项目当前直接使用 URI}returnuri// 已经是文件路径}八、权限相关8.1 相册读取权限使用 PhotoViewPicker 不需要申请ohos.permission.READ_MEDIA权限——系统选择器自动处理了授权。但如果使用photoAccessHelper的其他 API如遍历相册则需要声明权限{ requestPermissions: [ { name: ohos.permission.READ_MEDIA, reason: $string:media_permission_reason, usedScene: { abilities: [EntryAbility], when: inuse } } ] }8.2 权限对比场景需要权限权限声明PhotoViewPicker.select()❌ 不需要系统选择器自带授权photoAccessHelper.getAlbums()✅ 需要ohos.permission.READ_MEDIAphotoAccessHelper.addAsset()✅ 需要ohos.permission.WRITE_MEDIA总结本文从 CapturePage 的pickFromAlbum()方法出发完整解析了鸿蒙 PhotoViewPicker 的使用基本使用new PhotoViewPicker()→select(options)→result.photoUris选项配置maxSelectNumber控制选择数量双模式切换通过source参数在拍照/相册间切换URI 处理拍照返回文件路径相册返回 content:// URI权限优势PhotoViewPicker 不需要额外权限声明下一篇文章将介绍图片压缩与本地存储——在保存图片时的质量和大小优化。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力参考资源PhotoViewPicker API[CapturPage 相册选择源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/CapturePage.ets)photoAccessHelper 开发指南kit.MediaLibraryKit 模块media 权限声明HarmonyOS 开发文档九、PhotoViewPicker 完整功能扩展9.1 多选图片场景asyncfunctionpickMultipleImages():Promisestring[]{constphotoPickernewphotoAccessHelper.PhotoViewPicker()constoptions:photoAccessHelper.PhotoSelectOptions{maxSelectNumber:9,// 最多选 9 张MIME:[image/jpeg,image/png,image/heif]// 仅图片格式}constresultawaitphotoPicker.select(options)returnresult.photoUris??[]}9.2 DocumentViewPicker选择文档除了图片鸿蒙还提供了 DocumentViewPicker 用于选择文档文件import{picker}fromkit.CoreFileKitasyncfunctionpickDocument():Promisestring{constdocumentPickernewpicker.DocumentViewPicker()constoptions:picker.DocumentSelectOptions{maxSelectNumber:1,fileSuffixFilters:[.pdf,.txt,.docx]// 只显示指定后缀的文件}constresultawaitdocumentPicker.select(options)returnresult[0]??}9.3 URI 转文件路径相册 URIcontent://需要通过fs.open获取文件描述符才能读取importfsfromohos.file.fsasyncfunctionreadImageFromUri(uri:string):PromiseArrayBuffer{// 通过 URI 打开文件constfilefs.openSync(uri,fs.OpenMode.READ_ONLY)// 获取文件大小conststatfs.statSync(file.fd)// 读取文件内容constbuffernewArrayBuffer(stat.size)fs.readSync(file.fd,buffer)fs.closeSync(file)returnbuffer}9.4 相册访问能力对比功能PhotoViewPickerphotoAccessHelperDocumentViewPicker选择图片✅❌需权限查询❌选择文档❌❌✅遍历相册❌✅需 READ_MEDIA❌保存到相册❌✅需 WRITE_MEDIA❌权限要求无需额外权限需要 READ_MEDIA无需额外权限推荐场景用户主动选择程序化批量访问用户选择文档9.5 相册操作完整代码模板// 完整的相册选择 图片处理模板Componentstruct ImagePickerDemo{StateselectedImageUri:stringStateisLoading:booleanfalseprivateasyncpickAndProcess():Promisevoid{this.isLoadingtruetry{// ① 选择图片constpickernewphotoAccessHelper.PhotoViewPicker()constresultawaitpicker.select({maxSelectNumber:1})if(!result.photoUris||result.photoUris.length0)returnconsturiresult.photoUris[0]// ② 解码 PixelMapconstsourceimage.createImageSource(uri)constpixelMapawaitsource.createPixelMap({desiredSize:{width:1024,height:1024}})// ③ 压缩保存到缓存constoutputPath${getContext(this).cacheDir}/selected_${Date.now()}.jpgconstpackerimage.createImagePacker()constbufferawaitpacker.packing(pixelMap,{format:image/jpeg,quality:85})constfilefs.openSync(outputPath,fs.OpenMode.CREATE|fs.OpenMode.WRITE_ONLY)fs.writeSync(file.fd,buffer)fs.closeSync(file)// ④ 释放内存pixelMap.release()this.selectedImageUrioutputPath}catch(err){hilog.error(0x0000,ImagePicker,选图失败: %s,JSON.stringify(err))}finally{this.isLoadingfalse}}build(){Column(){if(this.selectedImageUri){Image(this.selectedImageUri).width(200).height(200).objectFit(ImageFit.Cover)}Button(从相册选择).onClick(()this.pickAndProcess())}}}如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力