1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import fs, { Filter, ConflictFiles } from '@ohos.file.fs'; 17import image from '@ohos.multimedia.image'; 18import UserFileManager from '@ohos.filemanagement.userFileManager'; 19import dataSharePredicates from '@ohos.data.dataSharePredicates'; 20 21import { Log } from '../utils/Log'; 22import DateTimeUtil from '../utils/DateTimeUtil'; 23import { FunctionCallBack, VideoCallBack } from './CameraService'; 24import ThumbnailGetter from './ThumbnailGetter'; 25import ReportUtil from '../utils/ReportUtil'; 26import { GlobalContext } from '../utils/GlobalContext'; 27import buffer from '@ohos.buffer'; 28 29const TAG = '[SaveCameraAsset]:'; 30 31export type FetchOpType = { 32 fetchColumns: Array<string>, 33 predicates: dataSharePredicates.DataSharePredicates, 34}; 35 36export default class SaveCameraAsset { 37 private lastSaveTime = ''; 38 private saveIndex = 0; 39 private mUserFileManager: UserFileManager.UserFileManager; 40 public videoPrepareFile: UserFileManager.FileAsset; 41 private mCameraAlbum: UserFileManager.Album; 42 private photoUri: string = ''; 43 private videoUri: string = ''; 44 45 constructor() { 46 this.mUserFileManager = UserFileManager.getUserFileMgr(GlobalContext.get().getCameraAbilityContext()); 47 } 48 49 public getPhotoUri() { 50 Log.log(`${TAG} getPhotoUri= ${this.photoUri}`); 51 return this.photoUri; 52 } 53 54 private async createCameraAlbum(): Promise<void> { 55 Log.log(`${TAG} createCameraAlbum E`); 56 if (!this.mCameraAlbum) { 57 let fetchResult = await this.mUserFileManager?.getAlbums(UserFileManager.AlbumType.SYSTEM, UserFileManager.AlbumSubType.CAMERA); 58 this.mCameraAlbum = await fetchResult?.getFirstObject(); 59 Log.log(`${TAG} createCameraAlbum albumUri: ${JSON.stringify(this.mCameraAlbum.albumUri)}`); 60 } 61 Log.log(`${TAG} createCameraAlbum X`); 62 } 63 64 public saveImage(mReceiver, thumbWidth: number, thumbHeight: number, thumbnailGetter: ThumbnailGetter, captureCallBack: FunctionCallBack): void { 65 Log.info(`${TAG} saveImage E mediaLibrary.getMediaLibrary media: ${this.mUserFileManager}`); 66 mReceiver.on('imageArrival', async () => { 67 Log.start(Log.UPDATE_PHOTO_THUMBNAIL); 68 Log.log(`${TAG} saveImage ImageReceiver on called`); 69 const buffer = await this.getBufferByReceiver(mReceiver); 70 if (!buffer) { 71 return; 72 } 73 captureCallBack.onCapturePhotoOutput(); 74 let pickerUri = GlobalContext.get().getPickerUri(); 75 Log.info('uri' + pickerUri) 76 if (pickerUri === '' || pickerUri === undefined) { 77 const fileAsset: UserFileManager.FileAsset = await this.createAsset(UserFileManager.FileType.IMAGE); 78 if (!fileAsset) { 79 Log.info(`${TAG} fileAsset is null`); 80 return; 81 } 82 // @ts-ignore 83 await fileAsset.setPending(true); 84 this.photoUri = fileAsset.uri; 85 Log.info(`${TAG} saveImage photoUri: ${this.photoUri}`); 86 await this.fileAssetOperate(fileAsset, async (fd: number) => { 87 Log.info(`${TAG} saveImage fileio write begin`); 88 try { 89 fs.writeSync(fd, buffer); 90 fs.fsyncSync(fd); 91 } catch (e) { 92 Log.error(`${TAG} fileAssetOperate fileio writeSync ${JSON.stringify(e)}`); 93 } 94 Log.info(`${TAG} saveImage fileio write done`); 95 }).catch(error => { 96 Log.error(`${TAG} saveImage error: ${JSON.stringify(error)}`); 97 }); 98 // @ts-ignore 99 await fileAsset.setPending(false); 100 thumbnailGetter.getThumbnailInfo(thumbWidth, thumbHeight, this.photoUri).then(thumbnail => { 101 Log.info(`${TAG} saveImage thumbnailInfo: ${thumbnail}`); 102 captureCallBack.onCaptureSuccess(thumbnail, this.photoUri); 103 Log.end(Log.UPDATE_PHOTO_THUMBNAIL); 104 }) 105 } else { 106 try { 107 Log.info(`${TAG} save Picker image`); 108 const file: fs.File = await fs.open(pickerUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 109 Log.info(`${TAG} open Picker image byteLength` + buffer.byteLength); 110 await fs.write(file.fd, buffer); 111 Log.info(`${TAG} write Picker image byteLength`); 112 captureCallBack.onCaptureSuccess('thumbnail', pickerUri); 113 } catch (e) { 114 captureCallBack.onCaptureSuccess('thumbnail', ''); 115 Log.error(`${TAG} Picker fileio writeSync error ${JSON.stringify(e)}`); 116 } 117 } 118 }) 119 Log.info(`${TAG} saveImage X`); 120 } 121 122 public async getThumbnailInfo(width: number, height: number): Promise<image.PixelMap | undefined> { 123 Log.info(`${TAG} getThumbnailInfo E width: ${width}, height: ${height}`); 124 Log.info(`${TAG} getThumbnailInfo E`); 125 const fileAsset: UserFileManager.FileAsset = await this.getLastFileAsset(); 126 if (!fileAsset) { 127 Log.info(`${TAG} getThumbnailInfo getLastFileAsset error: fileAsset undefined.`); 128 return undefined; 129 } 130 let thumbnailSize: image.Size = { 131 width: width, height: height 132 }; 133 let thumbnailPixelMap: image.PixelMap = <image.PixelMap> await fileAsset.getThumbnail(thumbnailSize).catch(e => { 134 Log.error(`${TAG} getThumbnail error: ${JSON.stringify(e)}`); 135 }); 136 if (thumbnailPixelMap === undefined) { 137 Log.info(`${TAG} getThumbnail fail`); 138 } else { 139 Log.info(`${TAG} getThumbnail successful ` + thumbnailPixelMap); 140 } 141 Log.info(`${TAG} getThumbnailInfo X`); 142 return thumbnailPixelMap; 143 } 144 145 public async getLastFileAsset(): Promise<UserFileManager.FileAsset> { 146 let predicates = new dataSharePredicates.DataSharePredicates(); 147 predicates.orderByDesc('date_added').limit(1, 0); 148 let fetchOptions: FetchOpType = { 149 fetchColumns: ['date_added'], 150 predicates: predicates, 151 }; 152 Log.info(`${TAG} getLastFileAsset fetchOp: ${JSON.stringify(fetchOptions)}`); 153 return this.getFileAssetByFetchOp(fetchOptions); 154 } 155 156 public async getFileAssetByFetchOp(fetchOp: FetchOpType): Promise<UserFileManager.FileAsset> { 157 let fetchResult; 158 let fileAsset; 159 try { 160 await this.createCameraAlbum(); 161 fetchResult = await this.mCameraAlbum?.getPhotoAssets(fetchOp); 162 if (fetchResult !== undefined) { 163 Log.info(`${TAG} getFileAssetByFetchOp fetchResult success`); 164 fileAsset = await fetchResult.getLastObject(); 165 if (fileAsset !== undefined) { 166 Log.info(`${TAG} getFileAssetByFetchOp fileAsset.displayName : ` + fileAsset.displayName); 167 } 168 } 169 } catch (e) { 170 Log.error(`${TAG} getFileAssetByFetchOp get fileAsset error: ${JSON.stringify(e)}`); 171 } finally { 172 fetchResult.close(); 173 } 174 return fileAsset; 175 } 176 177 private async fileAssetOperate(fileAsset: UserFileManager.FileAsset, operate: (fd: number) => void): Promise<void> { 178 const fd: number = <number> await fileAsset.open('Rw').catch(error => { 179 Log.error(`${TAG} fileAsset open error: ${JSON.stringify(error)}`); 180 return; 181 }); 182 try { 183 await operate.apply(this, [fd]); 184 } catch (error) { 185 Log.error(`${TAG} fileAsset operate error: ${JSON.stringify(error)}`); 186 } finally { 187 Log.info(`${TAG} fileAsset operate close`); 188 await fileAsset.close(fd).catch(error => { 189 ReportUtil.write(ReportUtil.SAVE_FAIL); 190 Log.error(`${TAG} fileAsset open error: ${JSON.stringify(error)}`); 191 }); 192 } 193 } 194 195 private async getBufferByReceiver(mReceiver: image.ImageReceiver): Promise<ArrayBuffer | undefined> { 196 const imageInfo: image.Image = <image.Image> await mReceiver.readNextImage().catch(error => { 197 Log.error(`${TAG} saveImage receiver read next image error: ${JSON.stringify(error)}`); 198 return undefined; 199 }); 200 try { 201 const img: image.Component = await imageInfo.getComponent(image.ComponentType.JPEG); 202 if (img && img.byteBuffer) { 203 const buffer: ArrayBuffer = img.byteBuffer.slice(0, img.byteBuffer.byteLength); 204 Log.info(`${TAG} saveImage getComponent img.byteBuffer.byteLength = ${buffer.byteLength}`); 205 return buffer; 206 } else { 207 Log.error(`${TAG} saveImage getComponent img is undefined error`); 208 } 209 } catch (error) { 210 Log.error(`${TAG} saveImage get buffer from receiver error: ${JSON.stringify(error)}`); 211 } finally { 212 if (imageInfo) { 213 await imageInfo.release().catch(error => { 214 Log.error(`${TAG} image info release error: ${JSON.stringify(error)}`); 215 }); 216 } 217 } 218 return undefined; 219 } 220 221 public async createAsset(type: UserFileManager.FileType): Promise<UserFileManager.FileAsset> { 222 const displayName = this.getDisplayName(type); 223 Log.info(`${TAG} createAsset displayName: ${displayName}`); 224 let option: UserFileManager.PhotoCreateOptions = { 225 subType: UserFileManager.PhotoSubType.CAMERA, 226 }; 227 let fileAsset: UserFileManager.FileAsset; 228 try { 229 fileAsset = await this.mUserFileManager.createPhotoAsset(displayName, option); 230 if (fileAsset !== undefined) { 231 Log.info(`${TAG} createPhotoAsset successfully displayName` + fileAsset.displayName); 232 } else { 233 Log.error(`${TAG} createPhotoAsset failed, fileAsset is undefined `); 234 } 235 } catch (e) { 236 Log.error(`${TAG} createPhotoAsset failed, error: ${JSON.stringify(e)}}`); 237 } 238 return fileAsset; 239 } 240 241 private getDisplayName(type: UserFileManager.FileType): string { 242 const mDateTimeUtil: DateTimeUtil = new DateTimeUtil(); 243 const mData: string = mDateTimeUtil.getDate(); 244 const mTime: string = mDateTimeUtil.getTime(); 245 if (type === UserFileManager.FileType.IMAGE) { 246 return `${this.checkName(`IMG_${mData}_${mTime}`)}.jpg`; 247 } else { 248 return `${this.checkName(`VID_${mData}_${mTime}`)}.mp4`; 249 } 250 } 251 252 public async createVideoFd(captureCallBack: VideoCallBack): Promise<number | undefined> { 253 Log.info(`${TAG} createVideoFd E`); 254 const fileAsset: UserFileManager.FileAsset = await this.createAsset(UserFileManager.FileType.VIDEO); 255 if (!fileAsset) { 256 Log.error(`${TAG} createVideoFd mediaLibrary createAsset error: fileAsset undefined.`); 257 return undefined; 258 } 259 let fdNumber: number = 0; 260 try { 261 this.videoPrepareFile = fileAsset; 262 this.videoUri = fileAsset.uri; 263 captureCallBack.videoUri(this.videoUri); 264 Log.info(`${TAG} SaveCameraAsset getLastObject.uri: ${JSON.stringify(fileAsset.uri)}`); 265 fdNumber = await fileAsset.open('Rw'); 266 } catch (err) { 267 Log.error(`${TAG} createVideoFd err: ${err}`); 268 } 269 Log.info(`${TAG} createVideoFd X`); 270 return fdNumber; 271 } 272 273 private checkName(name: string): string { 274 if (this.lastSaveTime === name) { 275 this.saveIndex++; 276 return `${name}_${this.saveIndex}`; 277 } 278 this.lastSaveTime = name; 279 this.saveIndex = 0; 280 return name; 281 } 282}