HarmonyOS7 相机拍照:Camera Kit 从预览到保存完整流程
2026/7/22 12:36:53 网站建设 项目流程

文章目录

    • 前言
    • Camera Kit 架构
    • 权限配置
    • 创建预览
    • 拍照实现
    • 保存到相册
    • 前后置切换
    • 踩坑记录
    • 写在最后

前言

相机开发,可以说是移动端最复杂的功能之一。HarmonyOS7 把相机能力收敛到 Camera Kit 里,架构比以前清晰了不少,但流程还是长——创建管理器、选摄像头、建会话、配流、启动预览、拍照、保存……少一步都不行。我第一次写的时候漏了commitConfig,预览死活出不来,排查了两小时。今天把完整流程捋一遍,每一步都有代码和讲解,照着走不会翻车。

Camera Kit 架构

Camera Kit 分三层,搞清层级关系才不会迷:

CameraManager ← 管理层:创建/获取所有相机对象 ├── CameraInput ← 输入层:选哪个摄像头(前/后) ├── PreviewOutput ← 输出层-预览流:画面实时显示 ├── PhotoOutput ← 输出层-拍照流:拍照后的图片数据 └── PhotoSession ← 会话层:把输入和输出串起来 ├── addInput() ├── addOutput() ├── commitConfig() └── start()

简单理解:CameraManager 是总管,CameraInput 是眼睛,PreviewOutput/PhotoOutput 是嘴巴,Session 把它们绑一起才能工作。

权限配置

相机开发要两个权限,module.json5里必须声明:

{"requestPermissions":[{"name":"ohos.permission.CAMERA"},{"name":"ohos.permission.WRITE_IMAGEVIDEO"}]}

CAMERA是拍照权限,WRITE_IMAGEVIDEO是保存到相册的权限。都要动态申请,静态声明不够:

import{abilityAccessCtrl,common}from'@kit.AbilityKit'asyncrequestPermissions():Promise<boolean>{constatManager=abilityAccessCtrl.createAtManager()constcontext=getContext(this)ascommon.UIAbilityContextconstpermissions=['ohos.permission.CAMERA','ohos.permission.WRITE_IMAGEVIDEO']constresult=awaitatManager.requestPermissionsFromUser(context,permissions)returnresult.authResults.every((r:number)=>r===0)}

别忘了在onPageShow或按钮点击时调这个方法。用户第一次弹授权弹窗可能拒绝,要做好拒绝后的提示处理。

创建预览

预览是相机开发的第一步,也是最关键的一步——预览出不来,后面全白搭。

import{camera}from'@kit.CameraKit'privatecameraManager:camera.CameraManager|null=nullprivatepreviewOutput:camera.PreviewOutput|null=nullprivatephotoOutput:camera.PhotoOutput|null=nullprivatesession:camera.PhotoSession|null=nullprivatesurfaceId:string=''asyncinitCamera(surfaceId:string){this.surfaceId=surfaceId// 1. 获取 CameraManagerconstcontext=getContext(this)ascommon.UIAbilityContextthis.cameraManager=camera.getCameraManager(context)// 2. 获取可用摄像头constcameras=this.cameraManager.getSupportedCameras()if(cameras.length===0){console.error('没有可用摄像头')return}constbackCamera=cameras[0]// 默认后置// 3. 创建 CameraInputconstcameraInput=this.cameraManager.createCameraInput(backCamera)cameraInput.open()// 4. 创建 PreviewOutputconstoutputCapability=this.cameraManager.getSupportedOutputCapability(backCamera)constpreviewProfile=outputCapability.previewProfiles[0]this.previewOutput=this.cameraManager.createPreviewOutput(previewProfile,surfaceId)// 5. 创建 PhotoOutputconstphotoProfile=outputCapability.photoProfiles[0]this.photoOutput=this.cameraManager.createPhotoOutput(photoProfile)// 6. 创建 Session 并配置this.session=this.cameraManager.createPhotoSession(cameraInput,this.previewOutput,this.photoOutput)awaitthis.session.commitConfig()awaitthis.session.start()}

关键讲解:

  • surfaceId从 XComponent 组件获取,用来承载预览画面。UI 层要放一个XComponent
    XComponent({id:'cameraXComponent',type:XComponentType.SURFACE,libraryname:''}).onLoad(()=>{this.initCamera(this.xComponentController.getXComponentSurfaceId())})
  • outputCapability拿到摄像头支持的配置,取第一个就行。想精细控制可以遍历选合适的分辨率
  • commitConfig()一定要调!我就是忘了这步,预览黑屏排查两小时。配置改了也得重新 commit

拍照实现

预览跑起来了,拍照就简单了——三步走:

// 1. 先注册照片可用回调setupPhotoCallback(){if(!this.photoOutput)returnthis.photoOutput.on('photoAvailable',(errCode:BusinessError,photo:camera.Photo)=>{if(errCode||!photo){console.error('拍照失败: '+errCode?.code)return}constimageObj=photo.main imageObj.getComponent(image.ComponentType.JPEG,(err,component)=>{if(err||!component?.byteBuffer)returnthis.saveToGallery(component.byteBuffer)imageObj.release()// ← 这行绝对不能漏!})})}// 2. 触发拍照takePhoto(){if(!this.photoOutput)returnconstsettings:camera.PhotoCaptureSetting={quality:camera.QualityLevel.QUALITY_LEVEL_HIGH,rotation:camera.ImageRotation.ROTATION_0,mirror:false}this.photoOutput.capture(settings)}

关键 API 讲解:

  • photoOutput.on('photoAvailable', ...):拍照成功后的数据回调。必须在拍照前注册,不然照片数据拿不到

  • photo.main:获取主图,还有photo.thumbnail可以拿缩略图

  • getComponent(image.ComponentType.JPEG):从 Image 对象提取 JPEG 数据,返回ArrayBuffer

  • imageObj.release():释放资源!不调会导致 BufferQueue 阻塞,后续拍照全部失败。这是最高频的坑

  • photoOutput.capture(settings):触发拍照。settings 可选,不传也行

保存到相册

拿到图片 Buffer 后,保存到系统相册:

import{photoAccessHelper}from'@kit.MediaLibraryKit'import{fileIo}from'@kit.CoreFileKit'asyncsaveToGallery(buffer:ArrayBuffer){consthelper=photoAccessHelper.getPhotoAccessHelper(getContext(this))consturi=awaithelper.createAsset(photoAccessHelper.PhotoType.IMAGE,'jpg')constfile=fileIo.openSync(uri,fileIo.OpenMode.READ_WRITE)fileIo.writeSync(file.fd,buffer)fileIo.closeSync(file.fd)console.info('保存成功: '+uri)}

createAsset在相册创建一条空记录并返回 URI,然后用fileIo把数据写进去。整个流程就是"先开个房间号,再把行李搬进去"。

前后置切换

切换摄像头不是简单换个 CameraInput,得重新建 Session:

@StateisBackCamera:boolean=trueasyncswitchCamera(){if(!this.cameraManager)return// 先停当前会话awaitthis.session?.stop()constcameras=this.cameraManager.getSupportedCameras()consttargetCamera=this.isBackCamera?cameras[1]:cameras[0]this.isBackCamera=!this.isBackCameraconstcameraInput=this.cameraManager.createCameraInput(targetCamera)awaitcameraInput.open()// 重建 Sessionthis.session=this.cameraManager.createPhotoSession(cameraInput,this.previewOutput!,this.photoOutput!)awaitthis.session.commitConfig()awaitthis.session.start()}

别想着只换 Input 不重建 Session——试过,不行。Session 绑定了 Input,得整体重建。

踩坑记录

表现解决
没调commitConfig()预览黑屏Session 配置完必须 commit
没调imageObj.release()第二次拍照失败回调里必须释放
没动态申请权限直接闪退requestPermissionsFromUser
surfaceId为空预览不显示在 XComponentonLoad里获取
后置摄像头不是 cameras[0]预览画面是前置通过cameraPosition筛选
权限拒绝后直接调相机崩溃先判断授权结果

写在最后

Camera Kit 的流程确实长,但每一步都有存在的理由。核心就是那张架构图——Manager → Input/Output → Session,搞清这个逻辑链,流程就不会乱。最坑的三个点我都踩过了:commitConfig 别忘、release 别忘、权限别忘

拍照只是相机功能的一部分,录像、闪光灯、对焦、变焦这些后面有机会再聊。下一篇换个轻松的话题——用加速度计做个摇一摇,几行代码就搞定。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询