完整代码
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { image } from '@kit.ImageKit';
import { fileIo } from '@kit.CoreFileKit';
import { display, Prompt } from '@kit.ArkUI';@Component
export struct CameraView {private context: Context | undefined = this.getUIContext().getHostContext();cameraHelper: CameraHelper = new CameraHelper(this.context!);aboutToAppear() {this.cameraHelper.setTakePictureCallback((path) => {Prompt.showToast({ message: path })})}@BuilderuiBuilder() {XComponent({id: 'cameraPreview',type: XComponentType.SURFACE,controller: this.cameraHelper.getController()}).onLoad(() => {this.cameraHelper.requestPermissionsFn(() => {this.cameraHelper.initCamera().then();});}).width("100%").height("100%").backgroundColor(Color.Black)}build() {this.uiBuilder()}
}class CameraHelper {private context: Context | undefined;constructor(context: Context) {this.context = context;}getScreenWidth(): number {return display.getDefaultDisplaySync().width;}getScreenHeight(): number {return display.getDefaultDisplaySync().height;}private sWidth: number = this.getScreenWidth()private sHeight: number = this.getScreenHeight()private session: camera.PhotoSession | undefined = undefined;private cameras: Array<camera.CameraDevice> = [];private cameraInput: camera.CameraInput | undefined = undefined;private previewOutput: camera.PreviewOutput | undefined = undefined;private photoOutput: camera.PhotoOutput | undefined = undefined;private selectCameraPosition: number = camera.CameraPosition.CAMERA_POSITION_FRONT;private onTakePictureCallback: ((imagePath: string) => void) | undefined = undefined;private cameraManager: camera.CameraManager | undefined = undefined;private xComponentSurfaceId: string = '';private xComponentCtl: XComponentController = new XComponentController();getController(): XComponentController {return this.xComponentCtl;}setTakePictureCallback(callback: (imagePath: string) => void): void {this.onTakePictureCallback = callback;}async requestPermissionsFn(success?: (result: boolean) => void): Promise<void> {let atManager = abilityAccessCtrl.createAtManager();if (this.context) {let res = await atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA']);for (let i = 0; i < res.permissions.length; i++) {if (res.permissions[i] === 'ohos.permission.CAMERA' && res.authResults[i] === 0) {success?.(true);}}}}async takePicture(): Promise<void> {if (!this.photoOutput) {Prompt.showToast({ message: 'PhotoOutput not initialized' })return;}try {let photoCaptureSetting: camera.PhotoCaptureSetting = {quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,rotation: camera.ImageRotation.ROTATION_0,mirror: this.isCameraFront()};console.info('开始拍照...');await this.photoOutput.capture(photoCaptureSetting);console.info('拍照成功完成');} catch (error) {let err = error as BusinessError;console.error(`拍照异常, error code: ${err.code}`);}}async releaseCamera(): Promise<void> {try {if (this.session) {await this.session.stop();this.session.release();this.session = undefined;}if (this.cameraInput) {await this.cameraInput.close();this.cameraInput = undefined;}this.photoOutput = undefined;this.previewOutput = undefined;} catch (error) {let err = error as BusinessError;console.error(`Release camera failed, error code: ${err.code}`);}}isCameraFront(): boolean {return this.selectCameraPosition === camera.CameraPosition.CAMERA_POSITION_FRONT;}private async saveArrayBufferToFile(buffer: ArrayBuffer, fileName: string): Promise<string> {const filePath = `${this.context!.filesDir}/${fileName}`;let file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);fileIo.writeSync(file.fd, buffer);fileIo.closeSync(file.fd);return filePath;}hasMultipleCameras(): boolean {return this.cameras && this.cameras.length > 1;}async switchCamera(): Promise<void> {if (!this.hasMultipleCameras()) {console.info('只有一个摄像头,无法切换');return;}try {if (this.isCameraFront()) {this.selectCameraPosition = camera.CameraPosition.CAMERA_POSITION_BACK;} else {this.selectCameraPosition = camera.CameraPosition.CAMERA_POSITION_FRONT;}await this.initCamera();} catch (error) {let err = error as BusinessError;console.error(`切换摄像头失败, error code: ${err.code}`);}}async initCamera(): Promise<void> {await this.releaseCamera();this.xComponentSurfaceId = this.xComponentCtl.getXComponentSurfaceId();try {this.cameraManager = camera.getCameraManager(this.context);if (!this.cameraManager) {console.error('getCameraManager failed');return;}this.cameras = this.cameraManager.getSupportedCameras();if (!this.cameras || this.cameras.length === 0) {console.error('No cameras found');return;}const cameraDevice = this.cameras.find((v) => {return v.cameraPosition === this.selectCameraPosition;})this.cameraInput = this.cameraManager.createCameraInput(cameraDevice);if (!this.cameraInput) {console.error('createCameraInput failed');return;}await this.cameraInput.open();let capability: camera.CameraOutputCapability =this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_PHOTO);if (!capability) {capability = this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_VIDEO);}if (!capability || capability.previewProfiles.length === 0) {console.error('No preview profiles supported');this.releaseCamera();return;}let previewProfile = this.selectBestPreviewProfile(capability.previewProfiles, this.sHeight, this.sWidth);this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId);if (!this.previewOutput) {console.error('createPreviewOutput failed');this.releaseCamera();return;}if (capability.photoProfiles && capability.photoProfiles.length > 0) {let photoProfile = this.selectBestPreviewProfile(capability.photoProfiles, this.sHeight, this.sWidth);this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile);}if (!this.photoOutput) {console.error('createPhotoOutput failed');this.releaseCamera();return;}this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;if (!this.session) {console.error('createSession failed');this.releaseCamera();return;}this.session.beginConfig();this.session.addInput(this.cameraInput);this.session.addOutput(this.previewOutput);this.session.addOutput(this.photoOutput);await this.session.commitConfig();await this.session.start();console.info('摄像头预览启动成功');this.setupPhotoOutputListeners();} catch (error) {let err = error as BusinessError;console.error(`Camera initialization failed, error code: ${err.code}`);}}private selectBestPreviewProfile(previewProfiles: camera.Profile[],targetWidth: number,targetHeight: number): camera.Profile {if (previewProfiles.length === 0) {throw new Error('No preview profiles available');}const targetAspectRatio = targetWidth / targetHeight;let bestProfile = previewProfiles[0];let minAspectDiff = Number.MAX_VALUE;let minSizeDiff = Number.MAX_VALUE;for (const profile of previewProfiles) {const profileAspectRatio = profile.size.width / profile.size.height;const aspectDiff = Math.abs(profileAspectRatio - targetAspectRatio);if (aspectDiff < minAspectDiff) {minAspectDiff = aspectDiff;bestProfile = profile;minSizeDiff = Math.abs(profile.size.width - targetWidth) +Math.abs(profile.size.height - targetHeight);}else if (Math.abs(aspectDiff - minAspectDiff) < 0.01) {const sizeDiff = Math.abs(profile.size.width - targetWidth) +Math.abs(profile.size.height - targetHeight);if (sizeDiff < minSizeDiff) {minSizeDiff = sizeDiff;bestProfile = profile;}}}console.info(`最佳匹配: ${bestProfile.size.width}x${bestProfile.size.height}, 宽高比: ${bestProfile.size.width /bestProfile.size.height}`);return bestProfile;}private setupPhotoOutputListeners(): void {if (!this.photoOutput) {return;}this.photoOutput.on('photoAvailable', (err: BusinessError, photo: camera.Photo) => {if (err) {console.error(`获取照片失败: ${err.code}`);return;}this.processPhoto(photo);});}private async processPhoto(photo: camera.Photo): Promise<void> {photo.main.getComponent(image.ComponentType.JPEG, async (err: BusinessError, component: image.Component) => {if (err || !component) {Prompt.showToast({ message: `获取JPEG组件失败: ${err?.code}` })return;}try {const fileName = `checkSkin${Date.now()}.jpg`;const buffer = component.byteBuffer;const filePath = await this.saveArrayBufferToFile(buffer, fileName);this.onTakePictureCallback?.(filePath)photo.main.release();} catch (error) {photo.main.release();}});}
}