From a21a8f025f56fb689cd7c1a4c95de72b2ea5d6ad Mon Sep 17 00:00:00 2001 From: liuhaikang Date: Thu, 6 Feb 2025 17:09:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96ImageKnifeLoader=E7=B1=BB?= =?UTF-8?q?=E7=9A=84getImageArrayBuffer=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liuhaikang --- library/src/main/ets/ImageKnifeDispatcher.ets | 4 +- library/src/main/ets/ImageKnifeLoader.ets | 177 +----------------- .../loaderStrategy/CustomLoaderStrategy.ets | 75 ++++++++ .../loaderStrategy/FileLocalLoadStrategy.ets | 62 ++++++ .../FileSystemLoaderStrategy.ets | 66 +++++++ .../ets/loaderStrategy/HttpLoaderStrategy.ets | 133 +++++++++++++ .../loaderStrategy/IImageLoaderStrategy.ets | 27 +++ .../ets/loaderStrategy/ImageLoaderFactory.ets | 44 +++++ .../loaderStrategy/ResourceLoaderStrategy.ets | 65 +++++++ 9 files changed, 480 insertions(+), 173 deletions(-) create mode 100644 library/src/main/ets/loaderStrategy/CustomLoaderStrategy.ets create mode 100644 library/src/main/ets/loaderStrategy/FileLocalLoadStrategy.ets create mode 100644 library/src/main/ets/loaderStrategy/FileSystemLoaderStrategy.ets create mode 100644 library/src/main/ets/loaderStrategy/HttpLoaderStrategy.ets create mode 100644 library/src/main/ets/loaderStrategy/IImageLoaderStrategy.ets create mode 100644 library/src/main/ets/loaderStrategy/ImageLoaderFactory.ets create mode 100644 library/src/main/ets/loaderStrategy/ResourceLoaderStrategy.ets diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets index bebdd22..573846a 100644 --- a/library/src/main/ets/ImageKnifeDispatcher.ets +++ b/library/src/main/ets/ImageKnifeDispatcher.ets @@ -214,9 +214,7 @@ export class ImageKnifeDispatcher { if((imageSrc as Resource).id != undefined) { moduleName = (imageSrc as Resource).moduleName src = (imageSrc as Resource).id - if(src == -1) { - resName = (imageSrc as Resource).params![0] - } + resName = (imageSrc as Resource).params![0] } else if(typeof imageSrc == 'string') { src = imageSrc } diff --git a/library/src/main/ets/ImageKnifeLoader.ets b/library/src/main/ets/ImageKnifeLoader.ets index a61d7d2..77ec8dd 100644 --- a/library/src/main/ets/ImageKnifeLoader.ets +++ b/library/src/main/ets/ImageKnifeLoader.ets @@ -36,6 +36,7 @@ import { FileTypeUtil } from './utils/FileTypeUtil'; import { DownsampleStrategy } from './downsampling/DownsampleStartegy'; import { Downsampler } from './downsampling/Downsampler'; import { common } from '@kit.AbilityKit'; +import { ImageLoaderFactory } from './loaderStrategy/ImageLoaderFactory'; class RequestData { receiveSize: number = 2000 @@ -445,178 +446,14 @@ export class ImageKnifeLoader { errorInfo: error }; - // 判断自定义下载 - if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC && typeof request.src == 'string') { - // 先从文件缓存获取 - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_CUSTOM_LOAD) - callBackTimeInfo.diskCheckStartTime = Date.now(); - resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) - callBackTimeInfo.diskCheckEndTime = Date.now(); - if (resBuf === undefined) { - LogUtil.log('start customGetImage src=' + request.componentId + ',srcType:' + request.requestSource + ',' + request.componentVersion) - const headerObj: Record = ImageKnifeLoader.getHeaderObj(request) - try { - request.customGetImage(request.context, request.src, headerObj) - .then((buffer)=>{ - if(buffer != undefined) { - ImageKnifeLoader.FileCacheParseImage(request,buffer,fileKey,callBackData) - } else { - loadError = 'customGetImage loadFail undefined' - ImageKnifeLoader.makeEmptyResult(request,loadError, ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_CUSTOM_LOAD, LoadPixelMapCode.IMAGE_CUSTOM_LOAD_FAILED_CODE)) - } - }).catch((err:string)=>{ - ImageKnifeLoader.makeEmptyResult(request,err, ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_CUSTOM_LOAD, LoadPixelMapCode.IMAGE_CUSTOM_LOAD_FAILED_CODE)) - }) - } catch (e) { - loadError = 'customGetImage loadFail failed' - ImageKnifeLoader.makeEmptyResult(request,loadError + e, ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_CUSTOM_LOAD, LoadPixelMapCode.IMAGE_CUSTOM_LOAD_FAILED_CODE)) - } - LogUtil.log('end customGetImage src=' + request.componentId + ',srcType:' + request.requestSource + ',' + request.componentVersion) - return - } - } - else { - if (typeof request.src === 'string') { - if (request.src.indexOf('http://') == 0 || request.src.indexOf('https://') == 0) { //从网络下载 - // 先从文件缓存获取 - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_NET) - LogUtil.log('get fileCache buffer start:' + request.componentId + ',srcType:' + request.requestSource + ',' + request.componentVersion) - callBackTimeInfo.diskCheckStartTime = Date.now() - resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) - callBackTimeInfo.diskCheckEndTime = Date.now() - LogUtil.log('get fileCache buffer end:' + request.componentId + ',srcType:' + request.requestSource + ',' + request.componentVersion) - if (resBuf !== undefined){ - LogUtil.log('success get image from filecache for key = ' + fileKey + ' src = ' + request.componentId + ',srcType:' + request.requestSource + ',' + request.componentVersion) - } - else if (request.onlyRetrieveFromCache != true) { - LogUtil.log('HttpDownloadClient.start:' + request.componentId + ',srcType:' + request.requestSource + ',' + request.componentVersion) - callBackTimeInfo.netRequestStartTime = Date.now(); - let httpRequest = http.createHttp(); - let progress: number = 0 - let arrayBuffers:ArrayBuffer[] = [] - const headerObj: Record = ImageKnifeLoader.getHeaderObj(request) - httpRequest.on('dataReceive', (data: ArrayBuffer) => { - arrayBuffers.push(data) - }); - - if (request.isWatchProgress) { - httpRequest.on('dataReceiveProgress', (data: RequestData) => { - // 下载进度 - if (data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number')) { - let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100) - if (progress !== percent) { - progress = percent - if (requestList === undefined) { - // 子线程 - emitter.emit(Constants.PROGRESS_EMITTER + request.memoryKey, { data: { 'value': progress } }) - }else { - // 主线程请求 - requestList!.forEach((requestWithSource: ImageKnifeRequestWithSource) => { - if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) { - requestWithSource.request.imageKnifeOption.progressListener(progress) - } - }) - } - } - } - }) - } - let promise = httpRequest.requestInStream(request.src, { - header: headerObj, - method: http.RequestMethod.GET, - expectDataType: http.HttpDataType.ARRAY_BUFFER, - connectTimeout: request.connectTimeout == undefined ? 60000 : request.connectTimeout, - readTimeout: request.readTimeout == undefined ? 30000 : request.readTimeout, - caPath: request.caPath === undefined ? undefined : request.caPath, - }); - - promise.then((data: number) => { - LogUtil.log('HttpDownloadClient.end:' + request.componentId + ',srcType:' + request.requestSource + ',' + request.componentVersion) - callBackData.httpCode = data - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_NET, undefined) - callBackTimeInfo.netRequestEndTime = Date.now(); - if (data == 200 || data == 206 || data == 204) { - resBuf = combineArrayBuffers(arrayBuffers) - ImageKnifeLoader.FileCacheParseImage(request,resBuf,fileKey, callBackData) - } else { - loadError = 'HttpDownloadClient has error, http code =' + JSON.stringify(data) - ImageKnifeLoader.makeEmptyResult(request,loadError, ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_NET, LoadPixelMapCode.IMAGE_HTTPS_LOAD_FAILED_CODE)) - } - }).catch((err: BusinessError) => { - callBackData.httpCode = err.code - loadError = 'HttpDownloadClient download ERROR : err = ' + JSON.stringify(err) - callBackTimeInfo.netRequestEndTime = Date.now(); - ImageKnifeLoader.makeEmptyResult(request,loadError, ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_NET, LoadPixelMapCode.IMAGE_HTTPS_LOAD_FAILED_CODE)) - }); - return - } - else { - callBackTimeInfo.netRequestEndTime = Date.now(); - loadError = 'onlyRetrieveFromCache,do not fetch image src = ' + request.src - } - } else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) { - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_SHARE_FILE) - await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => { - await fs.stat(file.fd).then(async (stat) =>{ - let buf = new ArrayBuffer(stat.size); - await fs.read(file.fd, buf).then((readLen) => { - resBuf = buf; - fs.closeSync(file.fd); - }).catch((err:BusinessError) => { - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_SHARE_FILE, LoadPixelMapCode.IMAGE_LOAD_SHARE_FILE_FAILED_CODE) - loadError = 'LoadDataShareFileClient fs.read err happened uri=' + request.src + ' err.msg=' + err?.message + ' err.code=' + err?.code - }) - }).catch((err:BusinessError) => { - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_SHARE_FILE, LoadPixelMapCode.IMAGE_LOAD_SHARE_FILE_FAILED_CODE) - loadError = 'LoadDataShareFileClient fs.stat err happened uri=' + request.src + ' err.msg=' + err?.message + ' err.code=' + err?.code - }) - }).catch((err:BusinessError) => { - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_SHARE_FILE, LoadPixelMapCode.IMAGE_LOAD_SHARE_FILE_FAILED_CODE) - loadError = 'LoadDataShareFileClient fs.open err happened uri=' + request.src + ' err.msg=' + err?.message + ' err.code=' + err?.code - }) - } else if (ImageKnifeLoader.isLocalLoadSrc(request.context, request.src)) { //从本地文件获取 - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_LOCAL_FILE) - try { - let stat = fs.statSync(request.src); - if (stat.size > 0) { - let file = fs.openSync(request.src, fs.OpenMode.READ_ONLY); - resBuf = new ArrayBuffer(stat.size); - fs.readSync(file.fd, resBuf); - fs.closeSync(file); - } - } catch (err) { - ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_LOCAL_FILE, LoadPixelMapCode.IMAGE_LOAD_LOCAL_FILE_FAILED_CODE) - loadError = 'LocalLoadSrc:' + request.src + ',err:' + err - } - } else { - loadError = 'Parameter not supported:' + request.src - } - } else if (typeof request.src == 'number') { //从资源文件获取 - let manager = request.context.createModuleContext(request.moduleName).resourceManager - if (resBuf == undefined && request.onlyRetrieveFromCache != true && request.requestSource == ImageKnifeRequestSource.SRC) { - if(request.src == -1) { - let resName = request.resName as string - resBuf = (await manager.getMediaByName(resName.substring(resName.lastIndexOf(".") + 1))).buffer as ArrayBuffer - } else { - resBuf = manager.getMediaContentSync(request.src).buffer as ArrayBuffer - } - } else if (resBuf == undefined && request.requestSource != ImageKnifeRequestSource.SRC) { - if(request.src == -1) { - let resName = request.resName as string - resBuf = (await manager.getMediaByName(resName.substring(resName.lastIndexOf(".") + 1))).buffer as ArrayBuffer - } else { - resBuf = manager.getMediaContentSync(request.src).buffer as ArrayBuffer - } - } - } - } - - if (resBuf === undefined){ + const loaderStrategy = ImageLoaderFactory.getLoaderStrategy(request); + if (loaderStrategy) { + await loaderStrategy.loadImage(request, requestList, fileKey, callBackData, callBackTimeInfo); + } else { + loadError = `Unsupported request type: ${request.src}`; callBackTimeInfo.requestEndTime = Date.now(); - ImageKnifeLoader.makeEmptyResult(request,loadError ,callBackData) - return + ImageKnifeLoader.makeEmptyResult(request, loadError, callBackData); } - ImageKnifeLoader.parseImage(resBuf,fileKey,request, callBackData) } static isLocalLoadSrc(context: Object | undefined, loadSrc: string): boolean { if (context != undefined) { diff --git a/library/src/main/ets/loaderStrategy/CustomLoaderStrategy.ets b/library/src/main/ets/loaderStrategy/CustomLoaderStrategy.ets new file mode 100644 index 0000000..c6d2bc0 --- /dev/null +++ b/library/src/main/ets/loaderStrategy/CustomLoaderStrategy.ets @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { FileCache } from '../cache/FileCache'; +import { IImageLoaderStrategy } from './IImageLoaderStrategy'; +import { ImageKnifeLoader } from '../ImageKnifeLoader'; +import { ImageKnifeData, ImageKnifeRequestWithSource, RequestJobRequest, TimeInfo } from '../model/ImageKnifeData'; +import { LoadPhase, LoadPixelMapCode } from '../utils/Constants'; +import { LogUtil } from '../utils/LogUtil'; +import List from '@ohos.util.List'; + +// 自定义加载策略 +export class CustomLoaderStrategy implements IImageLoaderStrategy { + async loadImage( + request: RequestJobRequest, + requestList: List | undefined, + fileKey: string, + callBackData: ImageKnifeData, + callBackTimeInfo: TimeInfo + ): Promise { + let resBuf: ArrayBuffer | undefined; + let loadError: string = ''; + + // 从文件缓存获取 + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_CUSTOM_LOAD); + callBackTimeInfo.diskCheckStartTime = Date.now(); + resBuf = FileCache.getFileCacheByFile(request.context, fileKey, request.fileCacheFolder); + callBackTimeInfo.diskCheckEndTime = Date.now(); + + if (resBuf !== undefined) { + ImageKnifeLoader.parseImage(resBuf, fileKey, request, callBackData); + } else if (!request.onlyRetrieveFromCache) { + LogUtil.log('start customGetImage src=' + request.componentId + ',srcType:' + request.requestSource + ',' + + request.componentVersion); + const headerObj: Record = ImageKnifeLoader.getHeaderObj(request); + try { + request.customGetImage!(request.context, request.src as string, headerObj) + .then((buffer)=>{ + if(buffer !== undefined && buffer !== null) { + ImageKnifeLoader.FileCacheParseImage(request,buffer,fileKey,callBackData); + } else { + loadError = 'customGetImage loadFail undefined'; + ImageKnifeLoader.makeEmptyResult(request,loadError, ImageKnifeLoader.assembleError(callBackData, + LoadPhase.PHASE_CUSTOM_LOAD, LoadPixelMapCode.IMAGE_CUSTOM_LOAD_FAILED_CODE)); + } + }).catch((err:string)=>{ + ImageKnifeLoader.makeEmptyResult(request,err, ImageKnifeLoader.assembleError(callBackData, + LoadPhase.PHASE_CUSTOM_LOAD, LoadPixelMapCode.IMAGE_CUSTOM_LOAD_FAILED_CODE)); + }) + } catch (e) { + loadError = 'customGetImage loadFail failed'; + ImageKnifeLoader.makeEmptyResult(request,loadError + e, ImageKnifeLoader.assembleError(callBackData, + LoadPhase.PHASE_CUSTOM_LOAD, LoadPixelMapCode.IMAGE_CUSTOM_LOAD_FAILED_CODE)); + } + LogUtil.log('end customGetImage src=' + request.componentId + ',srcType:' + + request.requestSource + ',' + request.componentVersion); + return; + } else { + loadError = `onlyRetrieveFromCache, do not fetch image src = ${request.src}`; + ImageKnifeLoader.makeEmptyResult(request, loadError, callBackData); + } + return; + } +} \ No newline at end of file diff --git a/library/src/main/ets/loaderStrategy/FileLocalLoadStrategy.ets b/library/src/main/ets/loaderStrategy/FileLocalLoadStrategy.ets new file mode 100644 index 0000000..4b0e2f1 --- /dev/null +++ b/library/src/main/ets/loaderStrategy/FileLocalLoadStrategy.ets @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { RequestJobRequest, ImageKnifeRequestWithSource, ImageKnifeData, TimeInfo } from '../model/ImageKnifeData'; +import { IImageLoaderStrategy } from './IImageLoaderStrategy'; +import List from '@ohos.util.List'; +import { ImageKnifeLoader } from '../ImageKnifeLoader'; +import { LoadPhase, LoadPixelMapCode } from '../utils/Constants'; +import fs from '@ohos.file.fs'; + +export class FileLocalLoadStrategy implements IImageLoaderStrategy { + loadImage(request: RequestJobRequest, requestList: List | undefined, fileKey: string, + callBackData: ImageKnifeData, callBackTimeInfo: TimeInfo): Promise { + let resBuf: ArrayBuffer | undefined; + let loadError: string = ''; + if (typeof request.src === 'string' && ImageKnifeLoader.isLocalLoadSrc(request.context, request.src)) { + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_LOCAL_FILE); + try { + const stat = fs.statSync(request.src); + if (stat.size > 0) { + const file = fs.openSync(request.src, fs.OpenMode.READ_ONLY); + resBuf = new ArrayBuffer(stat.size); + fs.readSync(file.fd, resBuf); + fs.closeSync(file); + } + } catch (err) { + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_LOCAL_FILE, + LoadPixelMapCode.IMAGE_LOAD_LOCAL_FILE_FAILED_CODE); + loadError = `LocalLoadSrc: ${request.src}, err: ${err}`; + ImageKnifeLoader.makeEmptyResult(request, loadError, + ImageKnifeLoader.assembleError( + callBackData, + LoadPhase.PHASE_LOCAL_FILE, + LoadPixelMapCode.IMAGE_LOAD_LOCAL_FILE_FAILED_CODE + ) + ); + } + } else { + loadError = `Parameter not supported: ${request.src}`; + ImageKnifeLoader.makeEmptyResult(request, loadError, callBackData); + } + + if (resBuf === undefined || resBuf === null) { + callBackTimeInfo.requestEndTime = Date.now(); + ImageKnifeLoader.makeEmptyResult(request, loadError, callBackData); + } else { + ImageKnifeLoader.parseImage(resBuf, fileKey, request, callBackData); + } + return Promise.resolve(); + } +} \ No newline at end of file diff --git a/library/src/main/ets/loaderStrategy/FileSystemLoaderStrategy.ets b/library/src/main/ets/loaderStrategy/FileSystemLoaderStrategy.ets new file mode 100644 index 0000000..8607471 --- /dev/null +++ b/library/src/main/ets/loaderStrategy/FileSystemLoaderStrategy.ets @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ImageKnifeData, RequestJobRequest, TimeInfo, ImageKnifeRequestWithSource } from '../model/ImageKnifeData'; +import fs from '@ohos.file.fs'; +import { IImageLoaderStrategy } from './IImageLoaderStrategy'; +import List from '@ohos.util.List'; +import { ImageKnifeLoader } from '../ImageKnifeLoader'; +import { LoadPhase, LoadPixelMapCode } from '../utils/Constants'; +import { BusinessError } from '@kit.BasicServicesKit'; + +export class FileSystemLoaderStrategy implements IImageLoaderStrategy { + async loadImage(request: RequestJobRequest, requestList: List | undefined, + fileKey: string, callBackData: ImageKnifeData, callBackTimeInfo: TimeInfo + ): Promise { + let resBuf: ArrayBuffer | undefined; + let loadError: string = ''; + + if (typeof request.src === 'string' && + (request.src.startsWith('datashare://') || request.src.startsWith('file://'))) { + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_SHARE_FILE); + await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => { + await fs.stat(file.fd).then(async (stat) => { + let buf = new ArrayBuffer(stat.size); + await fs.read(file.fd, buf).then((readLen) => { + resBuf = buf; + fs.closeSync(file.fd); + }).catch((err: BusinessError) => { + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_SHARE_FILE, + LoadPixelMapCode.IMAGE_LOAD_SHARE_FILE_FAILED_CODE) + loadError = 'LoadDataShareFileClient fs.read err happened uri=' + request.src + ' err.msg=' + err?.message + + ' err.code=' + err?.code + }) + }).catch((err: BusinessError) => { + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_SHARE_FILE, + LoadPixelMapCode.IMAGE_LOAD_SHARE_FILE_FAILED_CODE) + loadError = 'LoadDataShareFileClient fs.stat err happened uri=' + request.src + ' err.msg=' + err?.message + + ' err.code=' + err?.code + }) + }).catch((err: BusinessError) => { + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_SHARE_FILE, + LoadPixelMapCode.IMAGE_LOAD_SHARE_FILE_FAILED_CODE) + loadError = 'LoadDataShareFileClient fs.open err happened uri=' + request.src + ' err.msg=' + err?.message + + ' err.code=' + err?.code + }) + } + if (resBuf === undefined || resBuf === null) { + callBackTimeInfo.requestEndTime = Date.now(); + ImageKnifeLoader.makeEmptyResult(request, loadError, callBackData); + } else { + ImageKnifeLoader.parseImage(resBuf, fileKey, request, callBackData); + } + return; + } +} \ No newline at end of file diff --git a/library/src/main/ets/loaderStrategy/HttpLoaderStrategy.ets b/library/src/main/ets/loaderStrategy/HttpLoaderStrategy.ets new file mode 100644 index 0000000..83c802d --- /dev/null +++ b/library/src/main/ets/loaderStrategy/HttpLoaderStrategy.ets @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { FileCache } from '../cache/FileCache'; +import { IImageLoaderStrategy } from './IImageLoaderStrategy'; +import { + ImageKnifeData, + ImageKnifeRequestSource, + ImageKnifeRequestWithSource, + RequestJobRequest, + TimeInfo +} from '../model/ImageKnifeData'; +import http from '@ohos.net.http'; +import { ImageKnifeLoader } from '../ImageKnifeLoader'; +import { combineArrayBuffers } from '../utils/ArrayBufferUtils'; +import { BusinessError, emitter } from '@kit.BasicServicesKit'; +import { Constants, LoadPhase, LoadPixelMapCode } from '../utils/Constants'; +import { LogUtil } from '../utils/LogUtil'; +import List from '@ohos.util.List'; + +class RequestData { + public receiveSize: number = 2000 + public totalSize: number = 2000 +} + +// HTTP加载策略 +export class HttpLoaderStrategy implements IImageLoaderStrategy { + async loadImage( + request: RequestJobRequest, + requestList: List | undefined, + fileKey: string, + callBackData: ImageKnifeData, + callBackTimeInfo: TimeInfo + ): Promise { + let resBuf: ArrayBuffer | undefined; + let loadError: string = ''; + + // 从文件缓存获取 + ImageKnifeLoader.assembleError(callBackData, LoadPhase.PHASE_NET); + callBackTimeInfo.diskCheckStartTime = Date.now(); + resBuf = FileCache.getFileCacheByFile(request.context, fileKey, request.fileCacheFolder); + callBackTimeInfo.diskCheckEndTime = Date.now(); + + if (resBuf !== undefined) { + LogUtil.log(`success get image from filecache for key = ${fileKey} src = ${request.componentId}, + srcType:${request.requestSource}, ${request.componentVersion}`); + ImageKnifeLoader.parseImage(resBuf, fileKey, request, callBackData); + } else if (request.onlyRetrieveFromCache !== true) { + LogUtil.log(`HttpDownloadClient.start: ${request.componentId}, srcType:${request.requestSource}, + ${request.componentVersion}`); + callBackTimeInfo.netRequestStartTime = Date.now(); + const httpRequest = http.createHttp(); + let progress: number = 0; + const arrayBuffers: ArrayBuffer[] = []; + const headerObj: Record = ImageKnifeLoader.getHeaderObj(request); + + httpRequest.on('dataReceive', (data: ArrayBuffer) => { + arrayBuffers.push(data); + }); + + if (request.isWatchProgress) { + httpRequest.on('dataReceiveProgress', (data: RequestData) => { + if (data != undefined && typeof data.receiveSize === 'number' && typeof data.totalSize === 'number') { + const percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100); + if (progress !== percent) { + progress = percent; + if (requestList === undefined) { + // 子线程 + emitter.emit(Constants.PROGRESS_EMITTER + request.memoryKey, { data: { 'value': progress } }); + } else { + // 主线程请求 + requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => { + if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && + requestWithSource.source === ImageKnifeRequestSource.SRC) { + requestWithSource.request.imageKnifeOption.progressListener(progress); + } + }); + } + } + } + }); + } + let promise = httpRequest.requestInStream(request.src as string, { + header: headerObj, + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: request.connectTimeout === undefined || request.connectTimeout === null ? + 60000 : request.connectTimeout, + readTimeout: request.readTimeout === undefined || request.readTimeout === null? + 30000 : request.readTimeout, + caPath: request.caPath + }); + promise.then((data: number) => { + callBackData.httpCode = data; + ImageKnifeLoader.assembleError(callBackData,LoadPhase.PHASE_NET, undefined); + callBackTimeInfo.netRequestEndTime = Date.now(); + if (data == 200 || data == 206 || data == 204) { + resBuf = combineArrayBuffers(arrayBuffers); + ImageKnifeLoader.FileCacheParseImage(request,resBuf,fileKey, callBackData); + } else { + loadError = 'HttpDownloadClient has error, http code =' + JSON.stringify(data); + ImageKnifeLoader.makeEmptyResult(request,loadError, ImageKnifeLoader.assembleError(callBackData, + LoadPhase.PHASE_NET, LoadPixelMapCode.IMAGE_HTTPS_LOAD_FAILED_CODE)); + } + }).catch((err: BusinessError) => { + callBackData.httpCode = err.code; + loadError = 'HttpDownloadClient download ERROR : err = ' + JSON.stringify(err); + callBackTimeInfo.netRequestEndTime = Date.now(); + ImageKnifeLoader.makeEmptyResult(request,loadError, ImageKnifeLoader.assembleError(callBackData, + LoadPhase.PHASE_NET, LoadPixelMapCode.IMAGE_HTTPS_LOAD_FAILED_CODE)); + }); + LogUtil.log('HttpDownloadClient.end:' + request.componentId + ',srcType:' + + request.requestSource + ',' + request.componentVersion); + return; + } else { + callBackTimeInfo.netRequestEndTime = Date.now(); + loadError = `onlyRetrieveFromCache, do not fetch image src = ${request.src}`; + ImageKnifeLoader.makeEmptyResult(request, loadError, callBackData); + return; + } + } +} diff --git a/library/src/main/ets/loaderStrategy/IImageLoaderStrategy.ets b/library/src/main/ets/loaderStrategy/IImageLoaderStrategy.ets new file mode 100644 index 0000000..25348eb --- /dev/null +++ b/library/src/main/ets/loaderStrategy/IImageLoaderStrategy.ets @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ImageKnifeData, ImageKnifeRequestWithSource, RequestJobRequest, TimeInfo } from '../model/ImageKnifeData'; +import List from '@ohos.util.List'; + +// 定义图片加载策略接口 +export interface IImageLoaderStrategy { + loadImage( + request: RequestJobRequest, + requestList: List | undefined, + fileKey: string, + callBackData: ImageKnifeData, + callBackTimeInfo: TimeInfo + ): Promise; +} \ No newline at end of file diff --git a/library/src/main/ets/loaderStrategy/ImageLoaderFactory.ets b/library/src/main/ets/loaderStrategy/ImageLoaderFactory.ets new file mode 100644 index 0000000..67f26b4 --- /dev/null +++ b/library/src/main/ets/loaderStrategy/ImageLoaderFactory.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// 加载策略工厂 +import { CustomLoaderStrategy } from './CustomLoaderStrategy'; +import { FileSystemLoaderStrategy } from './FileSystemLoaderStrategy'; +import { HttpLoaderStrategy } from './HttpLoaderStrategy'; +import { IImageLoaderStrategy } from './IImageLoaderStrategy'; +import { ImageKnifeRequestSource, RequestJobRequest } from '../model/ImageKnifeData'; +import { ResourceLoaderStrategy } from './ResourceLoaderStrategy'; +import { FileLocalLoadStrategy } from './FileLocalLoadStrategy'; + +export class ImageLoaderFactory { + static getLoaderStrategy(request: RequestJobRequest): IImageLoaderStrategy | null { + if (request.customGetImage !== undefined && + request.requestSource === ImageKnifeRequestSource.SRC && + typeof request.src === 'string' + ) { + return new CustomLoaderStrategy(); + } else if (typeof request.src === 'string') { + if (request.src.startsWith('http://') || request.src.startsWith('https://')) { + return new HttpLoaderStrategy(); + } else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) { + return new FileSystemLoaderStrategy(); + } else { + return new FileLocalLoadStrategy(); + } + } else if (typeof request.src === 'number') { + return new ResourceLoaderStrategy(); + } + return null; + } +} \ No newline at end of file diff --git a/library/src/main/ets/loaderStrategy/ResourceLoaderStrategy.ets b/library/src/main/ets/loaderStrategy/ResourceLoaderStrategy.ets new file mode 100644 index 0000000..2e2773b --- /dev/null +++ b/library/src/main/ets/loaderStrategy/ResourceLoaderStrategy.ets @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// 资源文件加载策略 +import { IImageLoaderStrategy } from './IImageLoaderStrategy'; +import { + ImageKnifeData, + ImageKnifeRequestSource, + ImageKnifeRequestWithSource, + RequestJobRequest, + TimeInfo +} from '../model/ImageKnifeData'; +import List from '@ohos.util.List'; +import { application } from '@kit.AbilityKit'; +import { ImageKnifeLoader } from '../ImageKnifeLoader'; + +export class ResourceLoaderStrategy implements IImageLoaderStrategy { + async loadImage( + request: RequestJobRequest, + requestList: List | undefined, + fileKey: string, + callBackData: ImageKnifeData, + callBackTimeInfo: TimeInfo + ): Promise { + let resBuf: ArrayBuffer | undefined; + let loadError: string = ''; + + if (typeof request.src === 'number') { + const moduleContext = await application.createModuleContext(request.context, request.moduleName); + const manager = moduleContext.resourceManager; + if ((resBuf == undefined && request.onlyRetrieveFromCache !== true && + request.requestSource === ImageKnifeRequestSource.SRC) || + (resBuf == undefined && request.requestSource !== ImageKnifeRequestSource.SRC)) { + if (request.src === -1) { + const resName = request.resName as string; + resBuf = + (await manager.getMediaByName(resName.substring(resName.lastIndexOf('.') + 1))).buffer as ArrayBuffer; + } else { + resBuf = request.resName ? + manager.getRawFileContentSync(request.resName).buffer.slice(0) : + (manager.getMediaContentSync(request.src)).buffer as ArrayBuffer; + } + } + } + if (resBuf === undefined || resBuf === null){ + callBackTimeInfo.requestEndTime = Date.now(); + loadError = 'Resource load error'; + ImageKnifeLoader.makeEmptyResult(request, loadError ,callBackData); + } else { + ImageKnifeLoader.parseImage(resBuf,fileKey,request, callBackData); + } + return; + } +} \ No newline at end of file