HarmonyOS APP实战-基于Image Kit的图像处理APP - 第3篇:图片解码与元数据获取

📅 2026/7/13 13:56:03
HarmonyOS APP实战-基于Image Kit的图像处理APP - 第3篇:图片解码与元数据获取
HarmonyOS APP实战-基于Image Kit的图像处理APP - 第3篇图片解码与信息提取1. 开篇在第2篇中我们完成了图片选择与预览功能使用PhotoViewPicker让用户能够从相册中选择图片并通过Image组件在页面上展示缩略图。依赖注入的图片文件URI存储在全局状态中供后续功能调用。现在我们要让APP真正理解“这是一张什么样的图片”。用户选中图片后APP应当能够解析出图片的宽高、文件大小、格式等基本信息并在详情界面清晰展示。这些元数据是后续实现缩放、裁剪、滤镜等所有编辑操作的基础——比如裁剪前要知道图片有多宽多高缩放时要按比例计算。本篇的核心任务通过image.createImageSource解码图片使用ImageSource.createFromFile获取文件句柄提取ImageInfo中的宽高和格式信息。我们将创建ImageInfoModel数据模型统一管理这些信息设计ImageSourceUtils工具类封装解码逻辑最后在DecodeDemoPage页面中展示解码结果。2. 核心实现2.1 基础配置导入模块与权限声明在entry/src/main/ets/下创建model和utils目录。首先在model目录中定义ImageInfoModel类用于结构化存储图片解码后获取的信息。// entry/src/main/ets/model/ImageInfoModel.ets/** * 图片信息数据模型 * 封装从ImageInfo、文件大小等来源获取的图片元数据 */exportclassImageInfoModel{/** 图片宽度单位为像素 */width:number0;/** 图片高度单位为像素 */height:number0;/** 图片文件大小单位为字节 */size:number0;/** 图片格式如 image/jpeg、image/png */format:string;/** 图片文件路径用于标识来源 */uri:string;/** * 将ImageInfo对象的属性映射到模型 * param imageInfo 从ImageKit获取的ImageInfo对象 * param fileSize 文件字节大小 * param fileUri 文件路径 */publicfromImageInfo(imageInfo:image.ImageInfo,fileSize:number,fileUri:string):void{this.widthimageInfo.size.width;this.heightimageInfo.size.height;this.sizefileSize;this.formatimageInfo.mimeType||;this.urifileUri;}/** * 格式化文件大小显示保留一位小数 * returns 带单位的字符串如 1.5 MB / 800 KB */publicgetFormattedSize():string{if(this.size1024){returnthis.size B;}elseif(this.size1024*1024){return(this.size/1024).toFixed(1) KB;}else{return(this.size/(1024*1024)).toFixed(1) MB;}}/** * 获取格式显示名称 * returns 如 JPEG、PNG */publicgetFormatName():string{constmimethis.format;if(mime.includes(jpeg)||mime.includes(jpg)){returnJPEG;}elseif(mime.includes(png)){returnPNG;}elseif(mime.includes(bmp)){returnBMP;}elseif(mime.includes(webp)){returnWebP;}return未知格式;}}关键点说明ImageInfo对象包含sizePixelMapSize类型有width和height和mimeTypeMIME类型字符串。直接从文档API可知image.ImageInfo的定义中包含这两个字段。我们额外将文件大小和URI也纳入模型方便UI一次性展示。格式化大小和格式名称的方法便于前端直接绑定避免业务逻辑分散在页面中。2.2 核心逻辑ImageSourceUtils工具类ImageSourceUtils负责调用原始API解码图片。这里使用fs模块获取文件大小用image.createImageSource创建解码源。// entry/src/main/ets/utils/ImageSourceUtils.etsimportimagefromohos.multimedia.image;importfsfromohos.file.fs;import{ImageInfoModel}from../model/ImageInfoModel;/** * 图片源工具类 * 封装图片解码和元数据提取的通用方法 */exportclassImageSourceUtils{privateimageSource:image.ImageSource|nullnull;/** * 根据文件路径创建ImageSource并获取图片信息 * param fileUri 文件的URI格式为 file://xxx 或 /xxx * returns 包含宽高、格式、大小的ImageInfoModel */publicasyncgetImageInfo(fileUri:string):PromiseImageInfoModel{try{// 1. 通过文件路径创建ImageSource// 使用ImageSource.createFromFile直接传入文件路径this.imageSourceimage.ImageSource.createFromFile(fileUri);// 2. 获取ImageInfo对象// getImageInfo()返回PromiseImageInfoconstimageInfo:image.ImageInfoawaitthis.imageSource.getImageInfo();// 3. 获取文件大小// 使用fs.statSync获取文件属性conststatfs.statSync(fileUri);constfileSizestat.size;// 4. 填充模型constmodelnewImageInfoModel();model.fromImageInfo(imageInfo,fileSize,fileUri);returnmodel;}catch(error){console.error([ImageSourceUtils] 获取图片信息失败:${error.message});throwerror;}finally{// 5. 及时释放ImageSource占用的资源this.release();}}/** * 释放ImageSource资源 * 解引用对象让垃圾回收机制回收 */publicrelease():void{if(this.imageSource){this.imageSourcenull;}}}关键点说明ImageSource.createFromFile(fileUri)是官方文档中明确提供的静态工厂方法返回ImageSource实例。传参为字符串形式文件路径。getImageInfo()异步返回ImageInfo内部封装了图片解码的第一步读取文件头获取元数据。注意此方法不会解码完整像素数据性能开销较小。使用fs.statSync获取文件大小注意该方法同步执行不用担心阻塞UI线程。finally块中释放资源避免后续处理时持有无用的ImageSource实例导致内存泄漏。2.3 完整页面DecodeDemoPage将所有代码整合到DecodeDemoPage页面中。该页面展示用户选中的图片并显示解码后的完整信息。// entry/src/main/ets/pages/DecodeDemoPage.etsimportimagefromohos.multimedia.image;import{ImageInfoModel}from../model/ImageInfoModel;import{ImageSourceUtils}from../utils/ImageSourceUtils;importrouterfromohos.router;EntryComponentstruct DecodeDemoPage{// 接收跳转前页面的图片URIStateimageUri:string;StateimageInfo:ImageInfoModelnewImageInfoModel();StateisLoading:booleanfalse;StateerrorMsg:string;/** * 页面加载时获取传入的uri参数 */aboutToAppear():void{constparamsrouter.getParams()asRecordstring,Object;if(paramsparams[uri]){this.imageUriparams[uri]asstring;this.decodeImage();}}/** * 调用ImageSourceUtils执行解码和获取信息 */privateasyncdecodeImage():Promisevoid{if(!this.imageUri){this.errorMsg未指定图片文件;return;}this.isLoadingtrue;this.errorMsg;try{constutilsnewImageSourceUtils();constinfoawaitutils.getImageInfo(this.imageUri);this.imageInfoinfo;}catch(error){this.errorMsg解码失败:${error.message};console.error([DecodeDemoPage] decodeImage error:${error});}finally{this.isLoadingfalse;}}build(){Column(){// 标题栏Text(图片信息详情).fontSize(20).fontWeight(FontWeight.Bold).width(100%).textAlign(TextAlign.Center).margin({top:16,bottom:8})// 图片预览区域if(this.imageUri){Image(this.imageUri).width(100%).height(200).objectFit(ImageFit.Contain).margin({bottom:12})}// 加载状态if(this.isLoading){LoadingProgress().width(32).height(32).margin(24)}// 错误提示if(this.errorMsg){Text(this.errorMsg).fontColor(Color.Red).fontSize(14).margin(12)}// 信息展示卡片if(!this.isLoading!this.errorMsgthis.imageInfo.width0){Column(){this.buildInfoRow(图片宽度,${this.imageInfo.width}px)this.buildInfoRow(图片高度,${this.imageInfo.height}px)this.buildInfoRow(文件大小,this.imageInfo.getFormattedSize())this.buildInfoRow(MIME类型,this.imageInfo.format)this.buildInfoRow(格式名称,this.imageInfo.getFormatName())}.width(90%).padding(16).backgroundColor(Color.White).borderRadius(12).shadow({radius:6,color:#33000000,offsetY:2})}// 空状态提示if(!this.imageUri){Text(请通过前一页选择图片).fontColor(#888).margin(24)}}.width(100%).height(100%).backgroundColor(#F5F5F5).padding(16)}/** * 构建信息条目 * param label 标签名 * param value 数值 */BuilderprivatebuildInfoRow(label:string,value:string){Row(){Text(label).fontSize(16).fontColor(#333).fontWeight(FontWeight.Medium).width(120)Text(value).fontSize(16).fontColor(#666).width(100%)}.width(100%).height(40).border({width:{bottom:1},color:#E0E0E0})}}关键点说明页面通过router.getParams()接收上一页传递的uri参数确保选图功能和解码功能串联。decodeImage方法中用异步方式调用工具类防止阻塞UI线程显示LoadingProgress加载动画。使用buildInfoRow构建器批量生成信息行保持代码简洁。页面中对imageInfo.width 0的判读确保解码成功后才渲染卡片否则只显示错误或空状态。3. 运行验证运行APP从上一页通过PhotoViewPicker选择一张图片例如截取一张分辨率为1920×1080的JPEG照片点击确认后自动跳转到DecodeDemoPage。预期效果页面顶部显示图片预览缩略图通过Image组件加载原始URI。加载中会出现圆形进度条。解码完成后信息卡片清晰展示图片宽度1920 px图片高度1080 px文件大小例如 1.2 MB根据实际图片MIME类型image/jpeg格式名称JPEG若选择无效文件或路径则页面显示红色错误提示。4. 小结与预告本篇我们实现了图片解码与信息提取模块创建了ImageInfoModel数据模型、ImageSourceUtils工具类并设计了DecodeDemoPage页面。现在APP已经能从原始文件路径解码出图片的宽高、格式、大小等元数据这些信息将作为后续所有编辑功能的输入参数。下一篇我们将基于当前的图片解码能力实现缩放裁剪功能。具体来说我们将使用PixelMap的scale和crop方法配合滑块让用户调整缩放比例和裁剪区域最终生成新的PixelMap并在界面上实时预览效果。这将是APP第一个真正的图片编辑功能敬请期待。