HarmonyOS APP实战-基于Image Kit的图像处理APP - 第8篇:图片编码与保存
HarmonyOS APP实战-基于Image Kit的图像处理APP - 第8篇图片编码导出至相册1. 开篇在上一篇中我们实现了亮度、对比度、饱和度的调整功能。通过setColorMatrix方法用户可以在界面拖动滑块实时调整当前PixelMap的亮度增加或减少[4]位置值、对比度缩放[0][0]等对角元素和饱和度通过修改RGB通道权重。至此我们的图片处理APP已经具备了选择图片、缩放裁剪、旋转翻转、滤镜应用以及色彩参数调整等完整处理链路。但处理后的PixelMap始终停留在内存中一旦应用退出或页面关闭所有编辑成果都将丢失。本篇将实现图片编码与导出功能——使用image.createImagePacker将PixelMap编码为JPEG/PNG格式通过Media Library Kit写入系统相册让用户能够持久保存编辑后的图像。2. 核心实现2.1 基础配置首先需要在module.json5中声明存储权限并导入所需模块。从API 11开始媒体文件写入需要使用ohos.permission.WRITE_IMAGEVIDEO权限。// module.json5 权限配置片段{module:{requestPermissions:[{name:ohos.permission.WRITE_IMAGEVIDEO,// 写入图片/视频到相册reason:$string:permission_reason_save_image},{name:ohos.permission.READ_IMAGEVIDEO,// 读取相册中的图片reason:$string:permission_reason_read_image}]}}同时在页面顶部导入图像编码和文件操作所需模块import{image}fromkit.ImageKit;// Image Kit 图片处理能力import{fileIo}fromkit.CoreFileKit;// 文件读写操作import{common}fromkit.AbilityKit;// UIAbilityContext 用于获取上下文import{photoAccessHelper}fromkit.MediaLibraryKit;// 媒体库操作API 12import{BusinessError}fromkit.BasicServicesKit;// 错误类型定义关键点说明ohos.permission.WRITE_IMAGEVIDEO是写入媒体文件的必要权限需要在用户使用时动态授权photoAccessHelper在API 12后取代了旧的medialibrary接口提供更简洁的媒体文件创建方式使用kit.MediaLibraryKit导入时需要确保SDK版本不低于API 122.2 核心逻辑ImagePackerUtils创建一个工具类封装图片编码和文件保存的核心逻辑。这里使用image.createImagePacker进行编码并使用photoAccessHelper创建媒体资源。// utils/ImagePackerUtils.etsimport{image,fileIo,photoAccessHelper,common,BusinessError}from../utils/ImportManager;exportclassImagePackerUtils{privatepacker:image.ImagePackerimage.createImagePacker();/** * 将 PixelMap 编码为 JPEG 二进制数据 * param pixelMap 待编码的 PixelMap * param quality 编码质量0-100默认80 * returns 编码后的 ArrayBuffer */publicasyncencodeToJpeg(pixelMap:image.PixelMap,quality:number80):PromiseArrayBuffer{// 配置编码参数constpackOptions:image.PackingOption{format:image/jpeg,// 输出格式支持 image/jpeg 和 image/pngquality:quality// 编码质量数值越高画质越好};// 执行编码返回 ArrayBufferconstencodedData:ArrayBufferawaitthis.packer.packing(pixelMap,packOptions);returnencodedData;}/** * 编码并保存到系统相册 * param context UIAbilityContext 实例 * param pixelMap 待保存的 PixelMap * param fileName 文件名不含扩展名如 processed_20240101 * returns 保存成功后的文件 URI */publicasyncsaveToAlbum(context:common.UIAbilityContext,pixelMap:image.PixelMap,fileName:stringIMG_${Date.now()}):Promisestring{// 1. 编码为 JPEG 二进制数据constbuffer:ArrayBufferawaitthis.encodeToJpeg(pixelMap);// 2. 创建媒体库辅助对象constphAccessHelperphotoAccessHelper.getPhotoAccessHelper(context);// 3. 创建媒体文件 URI在相册中创建一个新图片文件consturiawaitphAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE,.jpg);// 4. 打开文件并写入数据constfilefileIo.openSync(uri,fileIo.OpenMode.WRITE_ONLY);try{// 写入编码后的二进制数据fileIo.writeSync(file.fd,buffer);}finally{// 确保文件关闭fileIo.closeSync(file.fd);}returnuri;}/** * 释放编码器资源 */publicrelease():void{this.packer.release();}}关键点说明image.createImagePacker()创建一个图片打包器用于将PixelMap编码为压缩格式packing(pixelMap, packOptions)异步执行编码format支持image/jpeg和image/pngquality参数仅对JPEG格式有效PNG格式会忽略此参数photoAccessHelper.createAsset()在相册中创建一个新的空媒体资源返回可写入的文件URI使用fileIo.writeSync写入二进制数据务必在finally块中关闭文件描述符2.3 完整页面ImageExportPage创建导出页面用户可以在处理完成后点击保存按钮触发编码与导出流程并显示结果提示。// pages/ImageExportPage.etsimport{image}fromkit.ImageKit;import{common,router,BusinessError}fromkit.AbilityKit;import{promptAction}fromkit.ArkUI;import{ImagePackerUtils}from../utils/ImagePackerUtils;import{EditHistoryManager}from../utils/EditHistoryManager;// 历史记录管理后续篇实现EntryComponentstruct ImageExportPage{// 接收来自上一页的编辑后图片数据StateprivatecurrentPixelMap:image.PixelMap|nullnull;StateprivateisExporting:booleanfalse;StateprivatesavedUri:string;privatepackerUtils:ImagePackerUtilsnewImagePackerUtils();privatecontext:common.UIAbilityContextgetContext(this)ascommon.UIAbilityContext;aboutToAppear():void{// 从路由参数获取 PixelMap通常通过 AppStorage或本地文件传递constparamsrouter.getParams()asRecordstring,Object;constpixelMapIndexparams?.[pixelMapKey]asnumber;if(pixelMapIndex!undefined){// 假设通过 AppStorage 或全局变量获取 PixelMapthis.currentPixelMapAppStorage.getimage.PixelMap(editingPixelMap);}}/** * 导出图片到相册 */asynconSaveToAlbum():Promisevoid{if(!this.currentPixelMap){promptAction.showToast({message:没有待保存的图片,duration:2000});return;}this.isExportingtrue;try{// 1. 编码并保存consturiawaitthis.packerUtils.saveToAlbum(this.context,this.currentPixelMap,PhotoEdit_${Date.now()});// 2. 保存成功后更新状态this.savedUriuri;this.isExportingfalse;// 3. 显示成功提示promptAction.showToast({message:图片已保存到相册,duration:2000});// 4. 记录本次导出到历史后续篇实现// EditHistoryManager.getInstance().addRecord({ type: export, pixelMap: this.currentPixelMap });}catch(error){this.isExportingfalse;consterrerrorasBusinessError;promptAction.showToast({message:保存失败:${err.message},duration:3000});console.error(ImageExport failed:,JSON.stringify(err));}}build(){Column(){// 显示当前编辑后的图片预览if(this.currentPixelMap){Image(this.currentPixelMap).width(100%).height(70%).objectFit(ImageFit.Contain).margin({bottom:10})}else{Text(暂无图片请先编辑).fontSize(18).fontColor(#666).textAlign(TextAlign.Center).margin({top:100})}// 保存按钮Button(保存到相册).width(80%).height(50).fontSize(18).fontWeight(FontWeight.Bold).backgroundColor(#007AFF).borderRadius(25).enabled(!this.isExportingthis.currentPixelMap!null).onClick(()this.onSaveToAlbum())// 导出状态提示if(this.isExporting){LoadingProgress().width(30).height(30).margin({top:10})Text(正在导出...).fontSize(14).fontColor(#999)}// 保存成功后的信息if(this.savedUri){Text(文件路径:${this.savedUri}).fontSize(12).fontColor(#666).margin({top:10}).textAlign(TextAlign.Center)}}.width(100%).height(100%).padding(20).backgroundColor(#F5F5F5)}}关键点说明router.getParams()用于获取前一个页面传递的参数这里假设通过key获取PixelMap索引AppStorage.getimage.PixelMap(editingPixelMap)从应用全局存储中获取当前编辑的PixelMappromptAction.showToast显示轻量级提示用于反馈保存成功或失败结果在onSaveToAlbum中先编码再保存并更新UI元素状态如按钮禁用、进度显示错误处理中使用BusinessError类型捕获避免应用崩溃3. 运行验证在应用中完成以下操作来验证导出功能打开APP选择一张图片进行任意编辑如调整亮度、应用滤镜、旋转等点击页面中的「保存到相册」按钮观察页面状态变化按钮变为禁用加载图标出现提示“正在导出…”等待片刻页面弹出Toast提示“图片已保存到相册”切换到系统相册应用检查最新图片是否出现在相册中图片内容与当前编辑效果一致如果权限未授权应用会弹出系统权限弹窗需用户点击允许若保存失败页面会显示错误提示如存储空间不足、权限拒绝等预期效果每次保存都会在相册生成一个新的JPEG文件文件名基于时间戳生成不会覆盖已有文件。4. 小结与预告本篇我们实现了图片编码导出功能。通过image.createImagePacker将PixelMap编码为JPEG格式利用photoAccessHelper.createAsset在系统相册创建媒体资源最后用fileIo.writeSync写入文件。这个功能使APP形成了完整的“编辑→导出”闭环用户的所有创意处理成果都能永久保存。同时我们在代码中为该页面后续集成历史记录功能预留了调用点保持代码的可扩展性。下一篇「批量处理与历史记录」将实现两个核心功能一是支持多图选择对每张图片应用相同的裁切或滤镜操作二是使用栈结构记录每次编辑操作支持撤销/重做。这两项功能将把我们的图片处理APP推向更专业的水平真正实现类Photoshop的编辑体验。敬请期待。

相关新闻