当前位置: 首页 > news >正文

HarmonyOS ArkWeb 系列之网页图片扫码识别:长按图片用 ScanKit 解码二维码

文章目录

      • 思路说明
      • 流程图
      • 完整代码实现
      • ScanKit 支持的码类型
      • 配合 enableMultiMode:识别一张图里的多个码
      • Promise 风格的调用方式
      • 注意:临时文件的清理
      • 写在最后

你有没有遇到这种需求:网页里有张图片是二维码,用户长按想扫描它——但系统相机无法识别网页截图,只能扫真实摄像头。ArkWeb + ScanKit 的组合可以优雅解决这个问题:长按图片,把图片下载到本地,然后调用detectBarcode.decode直接识别图片里的二维码,不需要打开摄像头。

思路说明

  1. 用户长按网页中的图片
  2. onContextMenuShow触发,通过getLastHitTest()拿到图片 URL
  3. 图片如果是 rawfile 本地资源,直接复制到filesDir
  4. 图片如果是网络资源,用 HTTP 下载到filesDir
  5. 调用detectBarcode.decode传入本地文件路径,识别二维码
  6. 把识别结果显示给用户

流程图

完整代码实现

import{webview}from'@kit.ArkWeb';import{common}from'@kit.AbilityKit';import{fileIoasfs}from'@kit.CoreFileKit';import{systemDateTime}from'@kit.BasicServicesKit';import{http}from'@kit.NetworkKit';import{scanCore,scanBarcode,detectBarcode}from'@kit.ScanKit';import{BusinessError}from'@kit.BasicServicesKit';@Entry@Componentstruct WebScanQRCodeDemo{// SaveButton 配置(用于其他功能,这里留一个标准配置)saveButtonOptions:SaveButtonOptions={icon:SaveIconStyle.FULL_FILLED,text:SaveDescription.SAVE_IMAGE,buttonType:ButtonType.Capsule};controller:webview.WebviewController=newwebview.WebviewController();@StateshowMenu:boolean=false;@StateimgUrl:string='';// 当前长按图片的 URL@StatedecodeResult:string='';// 扫码结果context=this.getUIContext().getHostContext()ascommon.UIAbilityContext;// ========== 复制 rawfile 图片到 filesDir ==========copyLocalPicToDir(rawfilePath:string,newFileName:string):string{constsrcFileDes=this.context.resourceManager.getRawFdSync(rawfilePath);constdstPath=this.context.filesDir+'/'+newFileName;constdest:fs.File=fs.openSync(dstPath,fs.OpenMode.CREATE|fs.OpenMode.READ_WRITE);letbufSize=4096;constbuf=newArrayBuffer(bufSize);letoffset=0;letreadLen=0;letlen=0;while((len=fs.readSync(srcFileDes.fd,buf,{offset:srcFileDes.offset+offset,length:bufSize}))!==0){readLen+=len;fs.writeSync(dest.fd,buf,{offset:offset,length:len});offset+=len;if((srcFileDes.length-readLen)<bufSize){bufSize=srcFileDes.length-readLen;}}fs.close(dest.fd);returndest.path;}// ========== 下载网络图片到 filesDir ==========asynccopyUrlPicToDir(picUrl:string,newFileName:string):Promise<string>{leturi='';consthttpRequest=http.createHttp();try{constdata:http.HttpResponse=await(httpRequest.request(picUrl)asPromise<http.HttpResponse>);if(data?.responseCode===http.ResponseCode.OK){constdstPath=this.context.filesDir+'/'+newFileName;constdest:fs.File=fs.openSync(dstPath,fs.OpenMode.CREATE|fs.OpenMode.READ_WRITE);fs.writeSync(dest.fd,data.resultasArrayBuffer);fs.close(dest.fd);uri=dstPath;}}finally{httpRequest.destroy();}returnuri;}// ========== 长按菜单 ==========@BuilderMenuBuilder(){Menu(){MenuItem({content:'扫描二维码'}).width(200).height(50).onClick(async()=>{try{letlocalFilePath='';// 处理图片来源if(this.imgUrl.includes('rawfile')){// 本地 rawfile 图片constrawFileName=this.imgUrl.substring(this.imgUrl.lastIndexOf('/')+1);localFilePath=this.copyLocalPicToDir(rawFileName,'qrcode_temp.png');}elseif(this.imgUrl.includes('http')||this.imgUrl.includes('https')){// 网络图片:先下载consttimestamp=systemDateTime.getTime();localFilePath=awaitthis.copyUrlPicToDir(this.imgUrl,`qrcode_${timestamp}.png`);}if(!localFilePath){this.decodeResult='无法获取图片文件';return;}// 配置扫码选项constscanOptions:scanBarcode.ScanOptions={scanTypes:[scanCore.ScanType.ALL],// 识别所有码类型enableMultiMode:true,// 支持多码识别enableAlbum:true};// 构造输入图片constinputImage:detectBarcode.InputImage={uri:localFilePath// 本地文件路径};// 调用识别接口(异步回调方式)detectBarcode.decode(inputImage,scanOptions,(error:BusinessError,results:Array<scanBarcode.ScanResult>)=>{if(error&&error.code){console.error(`识别失败:${error.code},${error.message}`);this.decodeResult=`识别失败(错误码:${error.code}`;return;}if(results&&results.length>0){// 把所有识别结果拼接显示this.decodeResult=results.map((r,i)=>`[${i+1}]${r.originalValue}`).join('\n');console.info('扫码结果:',JSON.stringify(results));}else{this.decodeResult='未识别到二维码';}});}catch(err){console.error(`扫码出错:${err.code},${err.message}`);this.decodeResult=`出错:${err.message}`;}})}}build(){Column(){Web({src:$rawfile('index.html'),controller:this.controller}).onContextMenuShow((event)=>{if(event){// 获取长按位置的图片信息consthitValue=this.controller.getLastHitTest();this.imgUrl=hitValue.extra;}this.showMenu=true;returntrue;}).bindContextMenu(this.MenuBuilder,ResponseType.LongPress).fileAccess(true).javaScriptAccess(true).domStorageAccess(true).height('70%')// 显示扫码结果if(this.decodeResult){Text('识别结果:').fontSize(14).fontWeight(FontWeight.Bold).margin({top:12,left:16})Text(this.decodeResult).fontSize(14).margin({top:4,left:16,right:16}).fontColor('#333').wordBreak(WordBreak.BREAK_ALL)}}.width('100%').height('100%')}}

ScanKit 支持的码类型

scanCore.ScanType枚举包含:

类型说明
ScanType.QR_CODE二维码(QR Code)
ScanType.DATA_MATRIXData Matrix 码
ScanType.PDF417PDF417 条形码
ScanType.AZTECAztec 码
ScanType.EAN_8EAN-8 条形码
ScanType.EAN_13EAN-13 条形码
ScanType.CODE_128Code 128 条形码
ScanType.ALL所有类型

配合 enableMultiMode:识别一张图里的多个码

constscanOptions:scanBarcode.ScanOptions={scanTypes:[scanCore.ScanType.QR_CODE],enableMultiMode:true,// 允许识别图片里的多个二维码enableAlbum:true};

enableMultiMode开启后,一张图片里有多个二维码的话,results数组会包含所有识别出来的码。

Promise 风格的调用方式

上面用了回调方式,也可以用 Promise:

try{constresults=awaitdetectBarcode.decode(inputImage,scanOptions);if(results.length>0){this.decodeResult=results[0].originalValue??'空结果';}}catch(err){this.decodeResult=`识别失败:${err.message}`;}

注意:临时文件的清理

每次扫码都会在filesDir里创建临时文件,用完最好删掉:

// 识别完成后清理临时文件try{fs.unlinkSync(localFilePath);}catch{// 删除失败不影响主流程}

写在最后

网页扫码这个功能在电商、票务类 App 里很实用——商品页里有二维码,用户不需要截图再打开相机扫,直接长按就能识别。核心是把网页图片拿到本地,然后交给 ScanKit 处理,整个流程清晰不绕弯。

参考文章

  • 《HarmonyOS ScanKit 扫码识别使用指南》
  • 《长按图片保存到相册:SaveButton + photoAccessHelper 实战》
  • 《detectBarcode.decode 图片识码 API 详解》
http://www.jsqmd.com/news/830568/

相关文章:

  • ADC选型新思路:从抗混叠架构革新到极致集成设计
  • AD21原理图设计避坑指南:搞定多通道编译时的‘多个网络名称’报错
  • 书匠策AI官网www.shujiangce.com:你的期刊论文“外挂“已上线,这波操作我真没见过!
  • Nuke Survival Toolkit:150+专业工具集的技术架构与实战深度解析
  • GPT4All-Chat终极解决方案:模型下载失败与对话卡顿专业修复指南
  • GreaterWMS:基于福特亚太区售后物流经验的开源仓库管理系统实战指南
  • ChatGPT对话数据迁移实战:从逆向工程到安全备份
  • win 中单独安装 mysql 客户端
  • 深度掌握SCSI设备管理:5个实战技巧解决存储运维难题
  • 别再死记硬背公式了!用Python手把手带你‘画’出GBDT的每一棵树(附完整代码)
  • 5分钟掌握Windows风扇控制:告别噪音,智能散热终极指南
  • 从 API Key 管理界面看 Taotoken 的团队协作与安全审计
  • 深度解析ChanlunX:开源缠论分析插件的完整实现指南
  • BackupPC-4.4.0 使用教程 - 2 备份文件
  • 嵌入式软件架构模式实战选型:从超级循环到RTOS与事件驱动
  • 中国资本主义工商业改造历史数据
  • taotoken平台openai兼容api快速接入python调用教程
  • 个人博客第五天
  • 别再死记硬背真值表了!用Multisim 14.1和Basys3 FPGA,手把手教你玩转数码管动态扫描(附完整工程文件)
  • 告别风扇噪音与高温:FanControl让你的Windows电脑安静又冷静
  • 基于辽宁科技大学的论文复现——从零开始SPMamba-yolo全流程部署文档
  • PXIe控制器:高性能测控系统的核心大脑与同步中枢
  • 深度解析Spreadsheets-are-all-you-need:用电子表格重新定义AI模型探索
  • 别再裸发ROS图像了!手把手教你用image_transport优化带宽(附压缩参数配置)
  • Fillinger智能填充插件:Adobe Illustrator自动化图案填充的终极解决方案
  • 【信息科学与工程学】【数据科学】数据科学领域-第三篇 数学基础10 对称性 (3)
  • League Akari:英雄联盟玩家的智能游戏助手
  • 2026年4月台灯厂家推荐,落地灯/黑板灯/教育照明/路灯/智能台灯/声光一体教室灯/台灯/教室灯/课桌椅,台灯公司实力 - 品牌推荐师
  • 读懂 SAP S/4HANA 里的 SAP Fiori 架构:前端服务器、搜索链路、传统应用接入与内容组织全景解析
  • 如何用嘎嘎降AI处理植物学论文:实验报告密集的植物学毕业论文降AI4.8元完整操作教程