Compare commits

...

3 Commits

Author SHA1 Message Date
zgf cab7c0f0f8
Pre Merge pull request !411 from zgf/3.x 2024-10-25 01:46:52 +00:00
zgf e641e99cb8 子线程网络请求和自定义网络改为异步请求
Signed-off-by: zgf <zenggaofeng2@h-partners.com>
2024-10-25 01:46:51 +00:00
zgf 1dde38f537 子线程网络请求和自定义网络改为异步请求
Signed-off-by: zgf <zenggaofeng2@h-partners.com>
2024-10-25 09:44:54 +08:00
17 changed files with 248 additions and 115 deletions

View File

@ -1,3 +1,9 @@
## 3.2.0-rc.0
- Rollback the old version V1 decorator. V2 decorator will be provided in version 4.x
- The sub-thread network request is changed to asynchronous, thereby increasing the number of concurrent sub-thread network requests
- Set the concurrency through the setMaxRequests interface under the ImageKnife class
- Custom network changed to asynchronous request
## 3.0.3 ## 3.0.3
- Released version 3.0.3 - Released version 3.0.3

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2024 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 { InfoItem } from './model/DataSourcePrefetching';
import { PageViewModel } from './model/PageViewModel';
import { ImageKnifeComponent, ImageKnife } from '@ohos/libraryimageknife';
import { CommonDataSource, GetSession } from './model/CommonDataSource';
import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
export struct CustomNetImagePage {
@State hotCommendList:CommonDataSource<InfoItem> = new CommonDataSource<InfoItem>([])
aboutToAppear(): void {
ImageKnife.getInstance().setMaxRequests(32)
this.hotCommendList.addData(this.hotCommendList.totalCount(),PageViewModel.getItems())
}
aboutToDisappear(): void {
ImageKnife.getInstance().setMaxRequests(8)
}
build() {
Column() {
WaterFlow() {
LazyForEach(this.hotCommendList,(item: InfoItem)=>{
FlowItem() {
Column(){
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: item.albumUrl,
placeholderSrc: $r("app.media.loading"),
errorholderSrc: $r("app.media.failed"),
customGetImage:custom
}
}).width("50%").height(200)
}
}.height(200)
.backgroundColor("#95efd2")
},(item: string) => item)
}.columnsTemplate("1fr 1fr")
.columnsGap(10)
.rowsGap(5)
.backgroundColor(0xFAEEE0)
.width("100%").height("100%")
}
}
}
// 自定义下载方法
@Concurrent
async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>): Promise<ArrayBuffer | undefined> {
return new Promise((resolve,reject)=>{
if (typeof src == "string") {
let session = GetSession.session
let req = new rcp.Request(src,"GET");
session.fetch(req).then((response)=>{
if(response.statusCode == 200) {
let buffer = response.body
resolve(buffer)
} else {
reject("rcp code:"+response.statusCode)
}
}).catch((err:BusinessError)=>{
reject("error rcp src:"+src+",err:"+JSON.stringify(err))
})
}
})
}

View File

@ -50,6 +50,11 @@ struct Index {
uri: 'pages/TestCommonImage', uri: 'pages/TestCommonImage',
}); });
}) })
Button($r('app.string.Customize_RCP_network')).margin({top:10}).onClick(()=>{
router.push({
uri: 'pages/CustomNetImagePage',
});
})
Button($r('app.string.Test_Task_error')).margin({top:10}).onClick(()=>{ Button($r('app.string.Test_Task_error')).margin({top:10}).onClick(()=>{
router.push({ router.push({
uri: 'pages/TestTaskResourcePage', uri: 'pages/TestTaskResourcePage',

View File

@ -109,7 +109,7 @@ struct LoadStatePage {
} }
// 自定义下载方法 // 自定义下载方法
@Concurrent @Concurrent
async function custom(context: Context, src: string | PixelMap | Resource): Promise<ArrayBuffer | undefined> { async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>): Promise<ArrayBuffer | undefined> {
console.info("ImageKnife:: custom download" + src) console.info("ImageKnife:: custom download" + src)
// 举例写死从本地文件读取,也可以自己请求网络图片 // 举例写死从本地文件读取,也可以自己请求网络图片
return undefined return undefined

View File

@ -128,10 +128,11 @@ struct SingleImage {
// 自定义下载方法 // 自定义下载方法
@Concurrent @Concurrent
async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record<string, Object>): Promise<ArrayBuffer | undefined> { async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>): Promise<ArrayBuffer | undefined> {
let refer = headers!["refer"] as string let refer = headers!["refer"] as string
console.info("ImageKnife:: custom download" + src,"refer:"+refer) console.info("ImageKnife:: custom download" + src,"refer:"+refer)
// 举例写死从本地文件读取,也可以自己请求网络图片 // 举例写死从本地文件读取,也可以自己请求网络图片
return context.resourceManager.getMediaContentSync($r("app.media.startIcon").id).buffer as ArrayBuffer let buffer = context.resourceManager.getMediaContentSync($r("app.media.startIcon").id).buffer as ArrayBuffer
return buffer
} }

View File

@ -60,8 +60,9 @@ struct TestSetCustomImagePage {
} }
} }
@Concurrent @Concurrent
async function custom(context: Context, src: string | PixelMap | Resource): Promise<ArrayBuffer | undefined> { async function custom(context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>): Promise<ArrayBuffer | undefined> {
console.info("ImageKnife:: custom download" + src) console.info("ImageKnife:: custom download" + src)
// 举例写死从本地文件读取,也可以自己请求网络图片 // 举例写死从本地文件读取,也可以自己请求网络图片
return context.resourceManager.getMediaContentSync($r("app.media.pngSample").id).buffer as ArrayBuffer let buffer = context.resourceManager.getMediaContentSync($r("app.media.pngSample").id).buffer as ArrayBuffer
return buffer
} }

View File

@ -12,6 +12,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { statfs } from '@kit.CoreFileKit'
import { rcp } from '@kit.RemoteCommunicationKit'
export class CommonDataSource <T> implements IDataSource { export class CommonDataSource <T> implements IDataSource {
private dataArray: T[] = [] private dataArray: T[] = []
private listeners: DataChangeListener[] = [] private listeners: DataChangeListener[] = []
@ -51,4 +54,7 @@ export class CommonDataSource <T> implements IDataSource {
listener.onDataAdd(index) listener.onDataAdd(index)
}) })
} }
}
export class GetSession {
static session:rcp.Session = rcp.createSession()
} }

View File

@ -503,6 +503,10 @@
{ {
"name": "disk", "name": "disk",
"value": "Disk" "value": "Disk"
},
{
"name": "Customize_RCP_network",
"value": "Customize RCP network request"
} }
] ]
} }

View File

@ -31,6 +31,7 @@
"pages/PrefetchAndPreload", "pages/PrefetchAndPreload",
"pages/TestCacheDataPage", "pages/TestCacheDataPage",
"pages/TestChangeColorPage", "pages/TestChangeColorPage",
"pages/TestLoadCancelListenerPage" "pages/TestLoadCancelListenerPage",
"pages/CustomNetImagePage"
] ]
} }

View File

@ -499,6 +499,10 @@
{ {
"name": "disk", "name": "disk",
"value": "磁盘" "value": "磁盘"
},
{
"name": "Customize_RCP_network",
"value": "自定义rcp网络请求"
} }
] ]
} }

View File

@ -14,7 +14,7 @@
"main": "index.ets", "main": "index.ets",
"repository": "https://gitee.com/openharmony-tpc/ImageKnife", "repository": "https://gitee.com/openharmony-tpc/ImageKnife",
"type": "module", "type": "module",
"version": "3.0.3", "version": "3.2.0-rc.0",
"dependencies": { "dependencies": {
"@ohos/gpu_transform": "^1.0.2" "@ohos/gpu_transform": "^1.0.2"
}, },

View File

@ -38,7 +38,7 @@ export class ImageKnife {
private _isRequestInSubThread: boolean = true; private _isRequestInSubThread: boolean = true;
//定义全局网络请求header map //定义全局网络请求header map
headerMap: Map<string, Object> = new Map<string, Object>(); headerMap: Map<string, Object> = new Map<string, Object>();
customGetImage: ((context: Context, src: string | PixelMap | Resource) => Promise<ArrayBuffer | undefined>) | undefined = undefined customGetImage: ((context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>) => Promise<ArrayBuffer | undefined>) | undefined = undefined
public static getInstance(): ImageKnife { public static getInstance(): ImageKnife {
if (!ImageKnife.instance) { if (!ImageKnife.instance) {
ImageKnife.instance = new ImageKnife(); ImageKnife.instance = new ImageKnife();
@ -482,10 +482,10 @@ export class ImageKnife {
* 全局设置自定义下载 * 全局设置自定义下载
* @param customGetImage 自定义请求函数 * @param customGetImage 自定义请求函数
*/ */
setCustomGetImage(customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record<string, Object>) => Promise<ArrayBuffer | undefined>) { setCustomGetImage(customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>) => Promise<ArrayBuffer | undefined>) {
this.customGetImage = customGetImage this.customGetImage = customGetImage
} }
getCustomGetImage(): undefined | ((context: Context, src: string | PixelMap | Resource,headers?: Record<string, Object>) => Promise<ArrayBuffer | undefined>){ getCustomGetImage(): undefined | ((context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>) => Promise<ArrayBuffer | undefined>){
return this.customGetImage return this.customGetImage
} }
} }

View File

@ -189,6 +189,16 @@ export class ImageKnifeDispatcher {
if(request.customGetImage == undefined) { if(request.customGetImage == undefined) {
request.customGetImage = ImageKnife.getInstance().getCustomGetImage() request.customGetImage = ImageKnife.getInstance().getCustomGetImage()
} }
emitter.on(Constants.CALLBACK_EMITTER + memoryKey,(data)=>{
emitter.off(Constants.CALLBACK_EMITTER + memoryKey)
let res = data?.data?.value as RequestJobResult | undefined
this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator);
if (isWatchProgress){
emitter.off(Constants.PROGRESS_EMITTER + memoryKey)
}
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc)
})
if (ImageKnife.getInstance().isRequestInSubThread){ if (ImageKnife.getInstance().isRequestInSubThread){
// 启动线程下载和解码主图 // 启动线程下载和解码主图
LogUtil.log("ImageKnife_DataTime_getAndShowImage_Task.start:" + currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage_Task.start:" + currentRequest.imageKnifeOption.loadSrc)
@ -202,13 +212,14 @@ export class ImageKnifeDispatcher {
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(subthread):" + currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(subthread):" + currentRequest.imageKnifeOption.loadSrc)
taskpool.execute(task).then((res: Object) => { taskpool.execute(task).then((res: Object) => {
this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); // this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator);
if (isWatchProgress){ // if (isWatchProgress){
emitter.off(Constants.PROGRESS_EMITTER + memoryKey) // emitter.off(Constants.PROGRESS_EMITTER + memoryKey)
} // }
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) // LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) // LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc)
}).catch((err: BusinessError) => { }).catch((err: BusinessError) => {
emitter.off(Constants.CALLBACK_EMITTER + memoryKey)
LogUtil.error("Fail to requestJob in sub thread src=" + imageSrc + " err=" + err) LogUtil.error("Fail to requestJob in sub thread src=" + imageSrc + " err=" + err)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc)
if (isWatchProgress){ if (isWatchProgress){
@ -219,11 +230,12 @@ export class ImageKnifeDispatcher {
}) })
} else { //主线程请求 } else { //主线程请求
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(mainthread):" + currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start(mainthread):" + currentRequest.imageKnifeOption.loadSrc)
requestJob(request, requestList).then((res: RequestJobResult | undefined) => { requestJob(request, requestList).then(() => {
this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); // this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator);
LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) // LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) // LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc)
}).catch((err: BusinessError) => { }).catch((err: BusinessError) => {
emitter.off(Constants.CALLBACK_EMITTER + memoryKey)
LogUtil.error("Fail to requestJob in main thread src=" + imageSrc + " err=" + err) LogUtil.error("Fail to requestJob in main thread src=" + imageSrc + " err=" + err)
LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:" + currentRequest.imageKnifeOption.loadSrc)
this.executingJobMap.remove(memoryKey); this.executingJobMap.remove(memoryKey);
@ -392,44 +404,15 @@ export class ImageKnifeDispatcher {
* @returns * @returns
*/ */
@Concurrent @Concurrent
async function requestJob(request: RequestJobRequest, requestList?: List<ImageKnifeRequestWithSource>): Promise<RequestJobResult | undefined> { async function requestJob(request: RequestJobRequest, requestList?: List<ImageKnifeRequestWithSource>) {
LogUtil.log("ImageKnife_DataTime_requestJob.start:" + request.src + " requestSource=" + request.requestSource) LogUtil.log("ImageKnife_DataTime_requestJob.start:" + request.src + " requestSource=" + request.requestSource)
let src = typeof request.src == "number" ? request.resName != undefined ? request.resName : request.src + "" : request.src let src = typeof request.src == "number" ? request.resName != undefined ? request.resName : request.src + "" : request.src
// 生成文件缓存key // 生成文件缓存key
let fileKey = request.engineKey.generateFileKey(src, request.signature, request.isAnimator) let fileKey = request.engineKey.generateFileKey(src, request.signature, request.isAnimator)
//获取图片资源 //获取图片资源
let resBuf: ArrayBuffer ImageKnifeLoader.execute(request,requestList,fileKey)
try {
LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.start:" + request.src)
resBuf = await ImageKnifeLoader.getImageArrayBuffer(request, requestList, fileKey)
LogUtil.log("ImageKnife_DataTime_requestJob.getImageArrayBuffer.end:" + request.src)
} catch (error) {
LogUtil.error("ImageKnife_DataTime_requestJob.end: getImageArrayBuffer error " + request.src + " err=" + error)
return ImageKnifeLoader.makeEmptyResult(error)
}
// 获取图片类型
let typeValue = new FileTypeUtil().getFileType(resBuf);
if(typeValue == null) {
LogUtil.log("ImageKnife_DataTime_requestJob.end: getFileType is null " + request.src)
return ImageKnifeLoader.makeEmptyResult("request is not a valid image source")
}
// 解析图片
LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.start:" + request.src)
let result: RequestJobResult = await ImageKnifeLoader.parseImage(resBuf, typeValue, fileKey, request)
LogUtil.log("ImageKnife_DataTime_requestJob.parseImage.end:" + request.src)
// 图形变化
if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined && result?.pixelMap !== undefined && typeof result.pixelMap !== 'string') {
LogUtil.log("ImageKnife_DataTime_requestJob.transform.start:" + request.src)
result.pixelMap = await request.transformation?.transform(request.context, result.pixelMap, request.componentWidth, request.componentHeight);
LogUtil.log("ImageKnife_DataTime_requestJob.transform.end:" + request.src)
}
LogUtil.log("ImageKnife_DataTime_requestJob.end:" + request.src)
return result
} }

View File

@ -28,6 +28,7 @@ import emitter from '@ohos.events.emitter';
import image from '@ohos.multimedia.image'; import image from '@ohos.multimedia.image';
import { RequestJobResult } from './model/ImageKnifeData' import { RequestJobResult } from './model/ImageKnifeData'
import util from '@ohos.util'; import util from '@ohos.util';
import { FileTypeUtil } from './utils/FileTypeUtil';
class RequestData { class RequestData {
receiveSize: number = 2000 receiveSize: number = 2000
@ -38,38 +39,52 @@ class RequestData {
* ImageKnifeDispatcher 抽取出来的方法,因@Concurrent只能import方法故抽取到另一个类 * ImageKnifeDispatcher 抽取出来的方法,因@Concurrent只能import方法故抽取到另一个类
*/ */
export class ImageKnifeLoader { export class ImageKnifeLoader {
static async parseImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, static execute(request: RequestJobRequest, requestList: List<ImageKnifeRequestWithSource> | undefined, fileKey: string){
request: RequestJobRequest): Promise<RequestJobResult> { ImageKnifeLoader.getImageArrayBuffer(request,requestList,fileKey)
}
static async parseImage(resBuf: ArrayBuffer, fileKey: string,
request: RequestJobRequest) {
let typeValue = new FileTypeUtil().getFileType(resBuf);
if(typeValue == null) {
LogUtil.log("ImageKnife_DataTime_requestJob.end: getFileType is null " + request.src)
ImageKnifeLoader.makeEmptyResult(request,"request is not a valid image source")
return
}
if(request.isAnimator) { if(request.isAnimator) {
return ImageKnifeLoader.parseForAnimatorComponent(resBuf ,typeValue ,fileKey, request) ImageKnifeLoader.parseForAnimatorComponent(resBuf ,typeValue ,fileKey, request)
return
} }
if (typeValue === 'gif' || typeValue === 'webp') { if (typeValue === 'gif' || typeValue === 'webp') {
return ImageKnifeLoader.parseAnimatorImage(resBuf ,typeValue ,fileKey , request) ImageKnifeLoader.parseAnimatorImage(resBuf ,typeValue ,fileKey , request)
return
} else if(typeValue == "svg") { } else if(typeValue == "svg") {
return ImageKnifeLoader.parseSvgImage(resBuf ,typeValue ,fileKey , request) ImageKnifeLoader.parseSvgImage(resBuf ,typeValue ,fileKey , request)
return
} }
return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request)
} }
static makeEmptyResult(error: string): RequestJobResult{ static makeEmptyResult(request:RequestJobRequest,error: string){
return { let res: RequestJobResult = {
pixelMap: undefined, pixelMap: undefined,
bufferSize: 0, bufferSize: 0,
fileKey: '', fileKey: '',
loadFail: error, loadFail: error,
} }
emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } })
} }
static async parseNormalImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, request: RequestJobRequest):Promise<RequestJobResult> { static async parseNormalImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, request: RequestJobRequest) {
let resPixelmap: PixelMap | undefined = undefined let resPixelmap: PixelMap | undefined = undefined
let decodingOptions: image.DecodingOptions = { let decodingOptions: image.DecodingOptions = {
editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false, editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false,
} }
let imageSource: image.ImageSource = image.createImageSource(resBuf) let imageSource: image.ImageSource = image.createImageSource(resBuf)
if (imageSource === undefined){ if (imageSource === undefined){
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed")
return
} }
let size = (await imageSource.getImageInfo()).size let size = (await imageSource.getImageInfo()).size
@ -79,23 +94,30 @@ export class ImageKnifeLoader {
imageSource.release() imageSource.release()
}).catch((error: BusinessError) => { }).catch((error: BusinessError) => {
imageSource.release() imageSource.release()
return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) ImageKnifeLoader.makeEmptyResult(request,JSON.stringify(error))
return
}) })
if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined && resPixelmap !== undefined) {
return { LogUtil.log("ImageKnife_DataTime_requestJob.transform.start:" + request.src)
resPixelmap = await request.transformation?.transform(request.context, resPixelmap, request.componentWidth, request.componentHeight);
LogUtil.log("ImageKnife_DataTime_requestJob.transform.end:" + request.src)
}
let res: RequestJobResult = {
pixelMap: resPixelmap, pixelMap: resPixelmap,
bufferSize: resBuf.byteLength, bufferSize: resBuf.byteLength,
fileKey: fileKey, fileKey: fileKey,
size:size, size:size,
type:typeValue type:typeValue
}; }
emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } })
} }
static async parseSvgImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string, static async parseSvgImage(resBuf: ArrayBuffer, typeValue: string, fileKey: string,
request: RequestJobRequest): Promise<RequestJobResult> { request: RequestJobRequest) {
let resPixelmap: PixelMap | undefined = undefined let resPixelmap: PixelMap | undefined = undefined
let imageSource: image.ImageSource = image.createImageSource(resBuf) let imageSource: image.ImageSource = image.createImageSource(resBuf)
if (imageSource === undefined){ if (imageSource === undefined){
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed")
return
} }
let size = (await imageSource.getImageInfo()).size let size = (await imageSource.getImageInfo()).size
@ -116,21 +138,23 @@ export class ImageKnifeLoader {
imageSource.release() imageSource.release()
}).catch((error: BusinessError) => { }).catch((error: BusinessError) => {
imageSource.release() imageSource.release()
return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) ImageKnifeLoader.makeEmptyResult(request,JSON.stringify(error))
return
}) })
let res: RequestJobResult = {
return {
pixelMap: resPixelmap, pixelMap: resPixelmap,
bufferSize: resBuf.byteLength, bufferSize: resBuf.byteLength,
fileKey: fileKey, fileKey: fileKey,
type:typeValue type:typeValue
}; }
emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } })
} }
static async parseAnimatorImage(resBuf: ArrayBuffer, typeValue: string, static async parseAnimatorImage(resBuf: ArrayBuffer, typeValue: string,
fileKey: string,request: RequestJobRequest): Promise<RequestJobResult> { fileKey: string,request: RequestJobRequest) {
let imageSource: image.ImageSource = image.createImageSource(resBuf) let imageSource: image.ImageSource = image.createImageSource(resBuf)
if (imageSource === undefined){ if (imageSource === undefined){
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed")
return
} }
let frameCount = await imageSource.getFrameCount() let frameCount = await imageSource.getFrameCount()
@ -140,22 +164,25 @@ export class ImageKnifeLoader {
if(frameCount == undefined || frameCount == 1) { if(frameCount == undefined || frameCount == 1) {
} else { } else {
let base64str = "data:image/" + typeValue + ";base64," + new util.Base64Helper().encodeToStringSync(new Uint8Array(resBuf)) let base64str = "data:image/" + typeValue + ";base64," + new util.Base64Helper().encodeToStringSync(new Uint8Array(resBuf))
return { let res: RequestJobResult = {
pixelMap: base64str, pixelMap: base64str,
bufferSize: resBuf.byteLength, bufferSize: resBuf.byteLength,
fileKey: fileKey, fileKey: fileKey,
size:size, size:size,
type:typeValue type:typeValue
}; }
emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } })
return
} }
return ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request) ImageKnifeLoader.parseNormalImage(resBuf, typeValue, fileKey, request)
} }
// 为AnimatorComponent解析动图 // 为AnimatorComponent解析动图
static async parseForAnimatorComponent(resBuf: ArrayBuffer, typeValue: string, fileKey: string,request: RequestJobRequest): Promise<RequestJobResult> { static async parseForAnimatorComponent(resBuf: ArrayBuffer, typeValue: string, fileKey: string,request: RequestJobRequest) {
if (typeValue === 'gif' || typeValue === 'webp') { if (typeValue === 'gif' || typeValue === 'webp') {
let imageSource: image.ImageSource = image.createImageSource(resBuf); let imageSource: image.ImageSource = image.createImageSource(resBuf);
if (imageSource === undefined){ if (imageSource === undefined){
return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed") ImageKnifeLoader.makeEmptyResult(request,"image.createImageSource failed")
return
} }
let decodingOptions: image.DecodingOptions = { let decodingOptions: image.DecodingOptions = {
editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false, editable: request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined ? true : false,
@ -179,9 +206,10 @@ export class ImageKnifeLoader {
}) })
}).catch((error: BusinessError) => { }).catch((error: BusinessError) => {
imageSource.release() imageSource.release()
return ImageKnifeLoader.makeEmptyResult(JSON.stringify(error)) ImageKnifeLoader.makeEmptyResult(request,JSON.stringify(error))
return
}) })
return { let res: RequestJobResult = {
pixelMap: "", pixelMap: "",
bufferSize: resBuf.byteLength, bufferSize: resBuf.byteLength,
fileKey: fileKey, fileKey: fileKey,
@ -189,8 +217,9 @@ export class ImageKnifeLoader {
pixelMapList, pixelMapList,
delayList delayList
} }
emitter.emit(Constants.CALLBACK_EMITTER + request.memoryKey, { data: { "value": res } })
} else { } else {
return ImageKnifeLoader.makeEmptyResult("ImageKnifeAnimatorComponent组件仅支持动态图") ImageKnifeLoader.makeEmptyResult(request,"ImageKnifeAnimatorComponent组件仅支持动态图")
} }
} }
static getHeaderObj(request:RequestJobRequest){ static getHeaderObj(request:RequestJobRequest){
@ -207,31 +236,40 @@ export class ImageKnifeLoader {
return headerObj return headerObj
} }
// 获取图片资源 // 获取图片资源
static async getImageArrayBuffer(request: RequestJobRequest, requestList: List<ImageKnifeRequestWithSource> | undefined,fileKey:string): Promise<ArrayBuffer> { static async getImageArrayBuffer(request: RequestJobRequest, requestList: List<ImageKnifeRequestWithSource> | undefined,fileKey:string) {
let resBuf: ArrayBuffer | undefined let resBuf: ArrayBuffer | undefined
let loadError: string = ""
// 判断自定义下载 // 判断自定义下载
if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC && typeof request.src == "string") { if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC && typeof request.src == "string") {
// 先从文件缓存获取 // 先从文件缓存获取
resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder) resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder)
if (resBuf === undefined) { if (resBuf === undefined) {
LogUtil.log("start customGetImage src=" + request.src) LogUtil.log("start customGetImage src=" + request.src)
const headerObj: Record<string, Object> = ImageKnifeLoader.getHeaderObj(request)
try { try {
const headerObj: Record<string, Object> = ImageKnifeLoader.getHeaderObj(request) request.customGetImage(request.context, request.src, headerObj)
resBuf = await request.customGetImage(request.context, request.src,headerObj) .then((buffer)=>{
LogUtil.log("end customGetImage src=" + request.src) if(buffer != undefined) {
} catch (err) { // 保存文件缓存
throw new Error('customGetImage loadFile failed! err = ' + err) if (request.writeCacheStrategy !== CacheStrategy.Memory) {
} LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:" + request.src)
if (resBuf === undefined) { FileCache.saveFileCacheOnlyFile(request.context, fileKey, buffer, request.fileCacheFolder)
throw new Error('customGetImage loadFile failed!') LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:" + request.src)
} }
// 保存文件缓存 ImageKnifeLoader.parseImage(buffer,fileKey,request)
if (request.writeCacheStrategy !== CacheStrategy.Memory) { } else {
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:" + request.src) loadError = "customGetImage loadFail undefined"
FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf, request.fileCacheFolder) ImageKnifeLoader.makeEmptyResult(request,loadError)
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:" + request.src) }
}).catch((err:string)=>{
ImageKnifeLoader.makeEmptyResult(request,err)
})
} catch (e) {
loadError = "customGetImage loadFail failed"
ImageKnifeLoader.makeEmptyResult(request,loadError + e)
} }
LogUtil.log("end customGetImage src=" + request.src)
return
} }
} }
else { else {
@ -284,25 +322,29 @@ export class ImageKnifeLoader {
// header: new Header('application/json') // header: new Header('application/json')
}); });
await promise.then((data: number) => { promise.then((data: number) => {
if (data == 200 || data == 206 || data == 204) { if (data == 200 || data == 206 || data == 204) {
resBuf = combineArrayBuffers(arrayBuffers) resBuf = combineArrayBuffers(arrayBuffers)
// 保存文件缓存
if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) {
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src)
FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder)
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src)
}
ImageKnifeLoader.parseImage(resBuf,fileKey,request)
} else { } else {
throw new Error("HttpDownloadClient has error, http code =" + JSON.stringify(data)) loadError = "HttpDownloadClient has error, http code =" + JSON.stringify(data)
ImageKnifeLoader.makeEmptyResult(request,loadError)
} }
}).catch((err: Error) => { }).catch((err: Error) => {
throw new Error("HttpDownloadClient download ERROR : err = " + JSON.stringify(err)) loadError = "HttpDownloadClient download ERROR : err = " + JSON.stringify(err)
ImageKnifeLoader.makeEmptyResult(request,loadError)
}); });
LogUtil.log("HttpDownloadClient.end:" + request.src) LogUtil.log("HttpDownloadClient.end:" + request.src)
// 保存文件缓存 return
if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) {
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src)
FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder)
LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src)
}
} }
else { else {
throw new Error('onlyRetrieveFromCache,do not fetch image src = ' + request.src) loadError = 'onlyRetrieveFromCache,do not fetch image src = ' + request.src
} }
} else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) { } else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) {
await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => { await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => {
@ -312,13 +354,13 @@ export class ImageKnifeLoader {
resBuf = buf; resBuf = buf;
fs.closeSync(file.fd); fs.closeSync(file.fd);
}).catch((err:BusinessError) => { }).catch((err:BusinessError) => {
throw new Error('LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) loadError = 'LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code
}) })
}).catch((err:BusinessError) => { }).catch((err:BusinessError) => {
throw new Error('LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) loadError = 'LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code
}) })
}).catch((err:BusinessError) => { }).catch((err:BusinessError) => {
throw new Error('LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code) loadError = 'LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code
}) })
} else { //从本地文件获取 } else { //从本地文件获取
try { try {
@ -330,7 +372,7 @@ export class ImageKnifeLoader {
fs.closeSync(file); fs.closeSync(file);
} }
} catch (err) { } catch (err) {
throw new Error(err) loadError = err
} }
} }
} else if (typeof request.src == "number") { //从资源文件获取 } else if (typeof request.src == "number") { //从资源文件获取
@ -354,8 +396,9 @@ export class ImageKnifeLoader {
} }
if (resBuf === undefined){ if (resBuf === undefined){
throw new Error('getImageArrayBuffer undefined') ImageKnifeLoader.makeEmptyResult(request,loadError)
return
} }
return resBuf ImageKnifeLoader.parseImage(resBuf,fileKey,request)
} }
} }

View File

@ -91,7 +91,7 @@ export interface RequestJobRequest {
allHeaders: Map<string, Object>, allHeaders: Map<string, Object>,
componentWidth: number, componentWidth: number,
componentHeight: number, componentHeight: number,
customGetImage?: (context: Context, src: string | PixelMap | Resource ,headers?: Record<string, Object>) => Promise<ArrayBuffer | undefined>, customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>) => Promise<ArrayBuffer | undefined>,
onlyRetrieveFromCache?: boolean onlyRetrieveFromCache?: boolean
requestSource: ImageKnifeRequestSource requestSource: ImageKnifeRequestSource
transformation?: PixelMapTransformation transformation?: PixelMapTransformation

View File

@ -61,7 +61,7 @@ export class ImageKnifeOption {
placeholderObjectFit?: ImageFit placeholderObjectFit?: ImageFit
// 错误图填充效果 // 错误图填充效果
errorholderObjectFit?: ImageFit errorholderObjectFit?: ImageFit
customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record<string, Object>) => Promise<ArrayBuffer | undefined> customGetImage?: (context: Context, src: string | PixelMap | Resource,headers?: Record<string,Object>) => Promise<ArrayBuffer | undefined>
border?: BorderOptions border?: BorderOptions
// 缓存策略 // 缓存策略
writeCacheStrategy?: CacheStrategy writeCacheStrategy?: CacheStrategy

View File

@ -14,4 +14,5 @@
*/ */
export class Constants { export class Constants {
public static PROGRESS_EMITTER: string = "progressEmitter" public static PROGRESS_EMITTER: string = "progressEmitter"
public static CALLBACK_EMITTER: string = "callBackEmitter"
} }