!25 新增2个功能:1.开发者可自定义缓存key 2.媒体图库uri可加载

Merge pull request !25 from zhoulisheng1/master
This commit is contained in:
openharmony_ci 2023-04-03 03:46:13 +00:00 committed by Gitee
commit 0c358fc58b
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
20 changed files with 492 additions and 129 deletions

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2023 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 { EngineKeyFactories, EngineKeyInterface, RequestOption } from '@ohos/imageknife'
export class CustomEngineKeyImpl implements EngineKeyInterface {
redefineUrl: (loadSrc: string) => string;
addOtherInfo: string = "Version=1.0.0;"
constructor() {
this.redefineUrl = this.urlNeedClearToken.bind(this);
}
// request只读
generateMemoryCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean): string {
return EngineKeyFactories.createMemoryCacheKey(loadSrc, size, transformed, dontAnimate, this.redefineUrl, this.addOtherInfo);
}
generateTransformedDiskCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean): string {
return EngineKeyFactories.createTransformedDiskCacheKey(loadSrc, size, transformed, dontAnimate, this.redefineUrl, this.addOtherInfo);
}
generateOriginalDiskCacheKey(loadSrc: string): string {
return EngineKeyFactories.createOriginalDiskCacheKey(loadSrc, this.redefineUrl, this.addOtherInfo);
}
// 需求场景: 请求图片可能 请求中存在token需要清除 可以把输入的url清除token后作为key的一部分这样token发生变化也能命中缓存。
urlNeedClearToken(url: string): string {
if (this.isHttpRequest(url)) {
return this.clearToken(url)
} else {
return url;
}
}
isHttpRequest(loadSrc: string) {
if (typeof loadSrc == "string" && loadSrc.toLowerCase().startsWith("http")) {
return true;
}
return false;
}
// 清除url里面中携带的token
clearToken(url: string): string {
let retUrl = url.replace(this.findTokenParam(url), "")
return retUrl;
}
// 网络图片加载 可能因为Token问题导致缓存失效
findTokenParam(url: string): string {
let tokenParam = "";
let tokenKeyIndex = url.indexOf("?token=") >= 0 ? url.indexOf("?token=") : url.indexOf("&token=");
if (tokenKeyIndex != -1) {
let nextAndIndex = url.indexOf("&", tokenKeyIndex + 1);
if (nextAndIndex != -1) {
tokenParam = url.substring(tokenKeyIndex + 1, nextAndIndex + 1);
} else {
tokenParam = url.substring(tokenKeyIndex);
}
}
return tokenParam;
}
}

View File

@ -1,7 +1,24 @@
/*
* Copyright (C) 2023 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 UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import { ImageKnife,ImageKnifeDrawFactory} from '@ohos/imageknife'
import { ImageKnife,ImageKnifeDrawFactory } from '@ohos/imageknife'
import { CustomEngineKeyImpl } from './CustomEngineKeyImpl'
import abilityAccessCtrl,{Permissions} from '@ohos.abilityAccessCtrl';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
@ -9,6 +26,8 @@ export default class EntryAbility extends UIAbility {
globalThis.ImageKnife = ImageKnife.with(this.context);
// 全局配置网络加载进度条
globalThis.ImageKnife.setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
// 全局配置缓存key
globalThis.ImageKnife.setEngineKeyImpl(new CustomEngineKeyImpl())
}
onDestroy() {
@ -19,6 +38,18 @@ export default class EntryAbility extends UIAbility {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
let list : Array<Permissions> = ['ohos.permission.MEDIA_LOCATION','ohos.permission.READ_MEDIA'];
let permissionRequestResult;
let atManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionsFromUser(this.context, list, (err,result)=>{
if(err){
console.log("dodo requestPermissionsFromUserError:"+JSON.stringify(err));
}else{
permissionRequestResult = result;
console.log("dodo permissionRequestResult:"+JSON.stringify(permissionRequestResult))
}
})
windowStage.loadContent('pages/index', (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');

View File

@ -12,16 +12,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ImageKnifeComponent} from '@ohos/imageknife'
import {ImageKnifeOption} from '@ohos/imageknife'
import {RotateImageTransformation} from '@ohos/imageknife'
import {RoundedCornersTransformation} from '@ohos/imageknife'
import {
ImageKnifeComponent,
ImageKnifeOption,
RotateImageTransformation,
RoundedCornersTransformation
} from '@ohos/imageknife'
@Entry
@Component
struct FrescoImageTestCasePage {
@State progresshint:string = "输出加载百分比回调信息"
struct CacheRuleChangedPage {
@State progresshint: string = "输出加载百分比回调信息"
@State imageKnifeOption1: ImageKnifeOption =
{
loadSrc: "https://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83ericA1Mv66TwicuYOtbDMBcUhv1aa9RJBeAn9uURfcZD0AUGrJebAn1g2AjN0vb2E1XTET7fTuLBNmA/132",
@ -50,6 +51,14 @@ struct FrescoImageTestCasePage {
{
loadSrc: "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB",
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
displayProgress: true,
};
@State imageKnifeOption5: ImageKnifeOption =
{
loadSrc: "https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp?mama=sdafsfasdfsdfsdaf&baba=sdfsafsafsd",
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
displayProgress: true,
@ -58,17 +67,35 @@ struct FrescoImageTestCasePage {
build() {
Scroll() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
// ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 })
// ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption })
// ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption3 })
Text('下图默认加载网络图片不带?token=').margin({ top: 20 })
Button('点击加载网络图片?token=').margin({ top: 5 }).onClick(() => {
this.imageKnifeOption4 = {
loadSrc: "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB?token=fsdafsfsafsafsdaf111111",
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
displayProgress: true,
}
})
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption4 })
.width(300)
.height(300)
// Scroll() {
// Text(this.progresshint).fontSize(15)
// }.width(300).height(200)
Text('下图默认加载网络图片不带&token=').margin({ top: 20 })
Button('点击加载网络图片&token=').margin({ top: 5 }).onClick(() => {
this.imageKnifeOption5 = {
loadSrc: "https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp?mama=sdafsfasdfsdfsdaf&token=fsdafsfsafsafsdaf111111&baba=sdfsafsafsd",
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed'),
displayProgress: true,
}
})
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption5 })
.width(300)
.height(300)
}
}
.width('100%')

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2023 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 mediaLibrary from '@ohos.multimedia.mediaLibrary';
import { ImageKnifeComponent, ImageKnifeOption, } from '@ohos/imageknife'
import ArkWorker from '@ohos.worker'
@Entry
@Component
struct DataShareUriLoadPage {
@State imageKnifeOption1: ImageKnifeOption =
{
loadSrc: $r('app.media.icon'),
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
};
private globalGifWorker: any = undefined
build() {
Scroll() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text("获取媒体图库的uri用ImageKnife展示").fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("点击加载Uri并展示")
.onClick(() => {
// 获取mediaLibrary实例后续用到此实例均采用此处获取的实例
const context = getContext(this);
let media = mediaLibrary.getMediaLibrary(context);
let fileKeyObj = mediaLibrary.FileKey;
let imageType = mediaLibrary.MediaType.IMAGE;
// 创建文件获取选项此处参数为获取image类型的文件资源
let imagesFetchOp = {
selections: fileKeyObj.MEDIA_TYPE + '= ?',
selectionArgs: [imageType.toString()],
};
// 获取文件资源使用callback方式返回异步结果
media.getFileAssets(imagesFetchOp, (error, fetchFileResult) => {
// 判断获取的文件资源的检索结果集是否为undefined若为undefined则接口调用失败
if (fetchFileResult == undefined) {
console.log('get fetchFileResult failed with error: ' + error);
return;
}
// 获取文件检索结果集中的总数
const count = fetchFileResult.getCount();
// 判断结果集中的数量是否小于0小于0时表示接口调用失败
if (count < 0) {
console.log('get count from fetchFileResult failed, count: ' + count);
return;
}
// 判断结果集中的数量是否等于0等于0时表示接口调用成功但是检索结果集为空请检查文件获取选项参数配置是否有误和设备中是否存在相应文件
if (count == 0) {
console.log('The count of fetchFileResult is zero');
return;
}
console.log('Get fetchFileResult successfully, count: ' + count);
// 获取文件检索结果集中的第一个资源使用callback方式返回异步结果
fetchFileResult.getFirstObject((error, fileAsset) => {
// 检查获取的第一个资源是否为undefined若为undefined则接口调用失败
if (fileAsset == undefined) {
console.log('get first object failed with error: ' + error);
return;
}
console.log("fileAsset id=" + fileAsset.id + " fileAsset uri=" + fileAsset.uri + " fileAsset displayName=" + fileAsset.displayName)
// 加载图库第一张图片的uri
this.imageKnifeOption1 = {
loadSrc: fileAsset.uri,
placeholderSrc: $r('app.media.icon_loading'),
errorholderSrc: $r('app.media.icon_failed')
}
// 释放FetchFileResult实例并使其失效。无法调用其他方法
fetchFileResult.close();
});
});
}).margin({ top: 5, left: 3 })
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300)
}.width('100%').backgroundColor(Color.Pink)
}
}
.width('100%')
.height('100%')
}
}

View File

@ -127,18 +127,23 @@ struct IndexFunctionDemo {
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
Text("测试fresco").fontSize(15)
Text("测试缓存规则和重试").fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("加载百分比")
Button("更改缓存规则")
.onClick(() => {
console.log("加载百分比")
router.push({ uri: "pages/frescoImageTestCasePage" });
console.log("更改缓存规则")
router.push({ uri: "pages/CacheRuleChangedPage" });
}).margin({ top: 5, left: 3 })
Button("重试Retry")
.onClick(() => {
console.log("重试Retry")
router.push({ uri: "pages/frescoRetryTestCasePage" });
}).margin({ top: 5, left: 3 })
Button("加载媒体库uri")
.onClick(() => {
console.log("加载媒体库uri")
router.push({ uri: "pages/dataShareUriLoadPage" });
}).margin({ top: 5, left: 3 })
}.width('100%').height(60).backgroundColor(Color.Pink)
Text("测试pngj和裁剪").fontSize(15)

View File

@ -46,7 +46,25 @@
"name": "ohos.permission.INTERNET",
"usedScene": {
"abilities": [
"FormAbility"
"EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.MEDIA_LOCATION",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.READ_MEDIA",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}

View File

@ -1,7 +1,7 @@
{
"src": [
"pages/index",
"pages/frescoImageTestCasePage",
"pages/CacheRuleChangedPage",
"pages/frescoRetryTestCasePage",
"pages/basicTestFeatureAbilityPage",
"pages/basicTestFileIOPage",
@ -23,6 +23,7 @@
"pages/cropImagePage2",
"pages/svgTestCasePage",
"pages/gifTestCasePage",
"pages/imageknifeTestCaseIndex"
"pages/imageknifeTestCaseIndex",
"pages/dataShareUriLoadPage"
]
}

View File

@ -27,6 +27,8 @@ export * from './src/main/ets/components/cache/diskstrategy/enum/AUTOMATIC'
export * from './src/main/ets/components/cache/diskstrategy/enum/DATA'
export * from './src/main/ets/components/cache/diskstrategy/enum/NONE'
export * from './src/main/ets/components/cache/diskstrategy/enum/RESOURCE'
export * from './src/main/ets/components/cache/key/EngineKeyInterface'
export * from './src/main/ets/components/cache/key/EngineKeyFactories'
/**
* compress

View File

@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fileio from '@ohos.fileio';
import fs from '@ohos.file.fs';
export class FileReader {
@ -32,8 +32,8 @@ export class FileReader {
return
}
try {
this.stream = fileio.createStreamSync(path, 'r+');
var stat = fileio.statSync(path)
this.stream = fs.createStreamSync(path, 'r+');
var stat = fs.statSync(path)
this.fileLength = stat.size
} catch (e) {
}

View File

@ -12,8 +12,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import resmgr from '@ohos.resourceManager'
import fileio from '@ohos.fileio';
import fs from '@ohos.file.fs';
export class FileUtils {
base64Str: string= ''
@ -37,7 +37,7 @@ export class FileUtils {
* @return number 文件句柄id
*/
createFile(path: string): number{
return fileio.openSync(path, 0o100, 0o666)
return fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd
}
/**
@ -45,7 +45,14 @@ export class FileUtils {
* @param path 文件绝对路径及文件名
*/
deleteFile(path: string):void {
fileio.unlinkSync(path);
try {
let fileExist = fs.accessSync(path);
if(fileExist) {
fs.unlinkSync(path);
}
}catch (err){
console.log("FileUtils deleteFile Method has error, err msg="+err.message + " err code="+err.code);
}
}
/**
@ -54,7 +61,7 @@ export class FileUtils {
*/
deleteFolderSync(path: string):void {
if (this.existFolder(path)) {
fileio.rmdirSync(path);
fs.rmdirSync(path);
}
}
@ -64,7 +71,7 @@ export class FileUtils {
*/
deleteFolderAsync(path: string, deleteComplete, deleteError) {
if (this.existFolder(path)) {
fileio.rmdir(path)
fs.rmdir(path)
.then(deleteComplete).catch(deleteError);
}
}
@ -74,14 +81,14 @@ export class FileUtils {
* @param path 文件绝对路径及文件名
*/
copyFile(oriPath: string, newPath: string) {
fileio.copyFileSync(oriPath, newPath);
fs.copyFileSync(oriPath, newPath);
}
/**
* 清空已有文件数据
*/
clearFile(path: string):number {
return fileio.openSync(path, 0o1000)
return fs.openSync(path, fs.OpenMode.TRUNC).fd
}
/**
@ -89,11 +96,11 @@ export class FileUtils {
*/
writeFile(path: string, content: ArrayBuffer | string) {
try {
let fd = fileio.openSync(path, 0o102, 0o666)
fileio.ftruncateSync(fd)
fileio.writeSync(fd, content)
fileio.fsyncSync(fd)
fileio.closeSync(fd)
let fd = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd
fs.truncateSync(fd)
fs.writeSync(fd, content)
fs.fsyncSync(fd)
fs.closeSync(fd)
} catch (e) {
console.log("FileUtils - Failed to writeFile for " + e)
}
@ -105,18 +112,18 @@ export class FileUtils {
writeData(path: string, content: ArrayBuffer | string) {
try {
console.info("FileUtils - writeData size 1= " + path)
let fd = fileio.openSync(path, 0o102, 0o666)
let fd = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd
console.info("FileUtils - writeData size 2= ")
let stat = fileio.statSync(path)
let stat = fs.statSync(path)
console.info("FileUtils - writeData size = " + stat.size)
fileio.writeSync(fd, content, { position: stat.size })
fs.writeSync(fd, content, { offset: stat.size })
let length = 0
if (content instanceof ArrayBuffer) {
length = content.byteLength
} else {
length = content.length
}
fileio.closeSync(fd)
fs.closeSync(fd)
} catch (e) {
console.log("FileUtils - Failed to writeData for " + e)
}
@ -127,7 +134,7 @@ export class FileUtils {
*/
exist(path: string): boolean{
try {
let stat = fileio.statSync(path)
let stat = fs.statSync(path)
return stat.isFile()
} catch (e) {
console.debug("FileUtils - fileutils exsit e" + e)
@ -151,7 +158,7 @@ export class FileUtils {
*/
getFileSize(path: string): number{
try {
let stat = fileio.statSync(path)
let stat = fs.statSync(path)
return stat.size
} catch (e) {
console.error("FileUtils - FileUtils getFileSize e " + e)
@ -164,14 +171,14 @@ export class FileUtils {
*/
readFilePic(path: string): ArrayBuffer {
try {
let stat = fileio.statSync(path)
let stat = fs.statSync(path)
console.info("FileUtils - readFilePic 1")
let fd = fileio.openSync(path, 0o2);
let length = fileio.statSync(path).size
let fd = fs.openSync(path, fs.OpenMode.READ_WRITE).fd;
let length = fs.statSync(path).size
console.info("FileUtils - readFilePic 2 length = " + length)
let buf = new ArrayBuffer(length);
console.info("FileUtils - readFilePic 3")
fileio.readSync(fd, buf)
fs.readSync(fd, buf)
return buf
} catch (e) {
console.log("FileUtils - readFilePic " + e)
@ -185,10 +192,10 @@ export class FileUtils {
*/
readStream(path: string): string {
try {
let stat = fileio.statSync(path)
let stat = fs.statSync(path)
let length = stat.size
let buf = new ArrayBuffer(length);
let ss = fileio.createStreamSync(path, "r+");
let ss = fs.createStreamSync(path, "r+");
ss.readSync(buf)
ss.closeSync();
return String.fromCharCode.apply(null, new Uint8Array(buf))
@ -206,7 +213,7 @@ export class FileUtils {
console.error("FileUtils - writeStream =1 ")
this.createFile(path)
console.error("FileUtils - writeStream 2 ")
let ss = fileio.createStreamSync(path, "r+");
let ss = fs.createStreamSync(path, "r+");
console.error("FileUtils - writeStream 3 " + tempArray.byteLength)
let num = ss.writeSync(tempArray, {
encoding: 'utf-8'
@ -226,7 +233,7 @@ export class FileUtils {
createFolder(path: string) {
//创建文件夹
if (!this.existFolder(path)) {
fileio.mkdirSync(path)
fs.mkdirSync(path)
}
}
@ -236,7 +243,7 @@ export class FileUtils {
*/
existFolder(path: string): boolean{
try {
let stat = fileio.statSync(path)
let stat = fs.statSync(path)
return stat.isDirectory()
} catch (e) {
console.debug("fileutils folder exsit error=" + e)

View File

@ -17,76 +17,50 @@ import {RequestOption} from '../../imageknife/RequestOption'
import {BaseTransform} from '../../imageknife/transform/BaseTransform'
export class EngineKey implements Key {
private request: RequestOption;
constructor(
request
) {
this.request = request;
}
// 内存缓存 缓存生成规则:是否会影响图片内容,不影响则通用(strategy onlyRetrieveFromCache isCacheable)为通用项目
// 生成规则 加载数据原 各类参数(排除监听 排除 占位图 失败占位图)
generateCacheKey(): string{
public static generateMemoryCacheKey(loadSrc:string,size:string,transformed:string,dontAnimate:boolean, redefine?:(loadSrc:string)=>string, otherInfo?:string): string{
let loadSrc = JSON.stringify(this.request.loadSrc);
let size = JSON.stringify(this.request.size);
let transformationsStr;
if(this.request && this.request.transformations) {
for (let i = 0; i < this.request.transformations.length; i++) {
if (i == this.request.transformations.length - 1) {
transformationsStr += this.request.transformations[i].getName() + "";
} else {
transformationsStr += this.request.transformations[i].getName() + ",";
}
}
if(redefine){
loadSrc = redefine(loadSrc);
}
let dontAnimateFlag = JSON.stringify(this.request.dontAnimateFlag);
let key = "loadSrc=" + loadSrc + ";" +
"size=" + size + ";" +
"transformations=" + transformationsStr + ";" +
"dontAnimateFlag=" + dontAnimateFlag + ";"
"transformations=" + transformed + ";" +
"dontAnimateFlag=" + dontAnimate + ";"
if(otherInfo){
key += otherInfo;
}
return key;
}
// 磁盘缓存 缓存生成规则:是否会影响图片内容,不影响则通用(strategy onlyRetrieveFromCache isCacheable)为通用项目
// 生成规则 加载数据原 各类参数(排除监听 排除 占位图 失败占位图)
generateResourceKey(): string{
let loadSrc = JSON.stringify(this.request.loadSrc);
let size = JSON.stringify(this.request.size);
let transformationsStr;
if(this.request && this.request.transformations) {
for (let i = 0; i < this.request.transformations.length; i++) {
if (i == this.request.transformations.length - 1) {
transformationsStr += this.request.transformations[i].getName() + "";
} else {
transformationsStr += this.request.transformations[i].getName() + ",";
}
}
public static generateTransformedDiskCacheKey(loadSrc:string,size:string,transformed:string,dontAnimate:boolean, redefine?:(loadSrc:string)=>string, otherInfo?:string): string{
if(redefine){
loadSrc = redefine(loadSrc);
}
let dontAnimateFlag = JSON.stringify(this.request.dontAnimateFlag);
let key = "loadSrc=" + loadSrc + ";" +
"size=" + size + ";" +
"transformations=" + transformationsStr + ";" +
"dontAnimateFlag=" + dontAnimateFlag + ";"
"transformations=" + transformed + ";" +
"dontAnimateFlag=" + dontAnimate + ";"
if(otherInfo){
key += otherInfo;
}
return key;
}
// 磁盘缓存
// 生成网络加载数据 原始数据存于磁盘的key
generateDataKey(): string{
let loadSrc = JSON.stringify(this.request.loadSrc);
public static generateOriginalDiskCacheKey(loadSrc:string, redefine?:(loadSrc:string)=>string, otherInfo?:string): string{
if(redefine){
loadSrc = redefine(loadSrc);
}
let key = "loadSrc=" + loadSrc + ";"
if(otherInfo){
key += otherInfo;
}
return key;
}

View File

@ -13,24 +13,39 @@
* limitations under the License.
*/
import {EngineKey} from '../key/EngineKey'
import {EngineKeyInterface} from '../key/EngineKeyInterface'
import {RequestOption} from '../../imageknife/RequestOption'
export class EngineKeyFactories {
buildCacheKey(
request) {
return new EngineKey(request).generateCacheKey();
export class EngineKeyFactories implements EngineKeyInterface {
// 生成内存缓存
generateMemoryCacheKey(loadSrc:string,size:string,transformed:string,dontAnimate:boolean) {
return EngineKey.generateMemoryCacheKey(loadSrc,size,transformed,dontAnimate);
}
// 生成原图变换后的图片的磁盘缓存
generateTransformedDiskCacheKey(loadSrc:string,size:string,transformed:string,dontAnimate:boolean) {
return EngineKey.generateTransformedDiskCacheKey(loadSrc,size,transformed,dontAnimate);
}
// 生成原图的磁盘缓存
generateOriginalDiskCacheKey(loadSrc:string) {
return EngineKey.generateOriginalDiskCacheKey(loadSrc);
}
buildDataKey(
request
) {
return new EngineKey(request).generateDataKey()
public static createMemoryCacheKey(loadSrc:string,size:string,transformed:string,dontAnimate:boolean, redefineUrl?:(loadSrc:string)=>string, addOtherInfo?:string):string{
return EngineKey.generateMemoryCacheKey(loadSrc,size,transformed,dontAnimate,redefineUrl,addOtherInfo);
}
buildResourceKey(
request
) {
return new EngineKey(request).generateResourceKey()
public static createTransformedDiskCacheKey(loadSrc:string,size:string,transformed:string,dontAnimate:boolean, redefineUrl?:(loadSrc:string)=>string, addOtherInfo?:string):string {
return EngineKey.generateTransformedDiskCacheKey(loadSrc,size,transformed,dontAnimate,redefineUrl,addOtherInfo);
}
public static createOriginalDiskCacheKey(loadSrc:string, redefineUrl?:(loadSrc:string)=>string, addOtherInfo?:string):string {
return EngineKey.generateOriginalDiskCacheKey(loadSrc,redefineUrl,addOtherInfo);
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 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 { RequestOption } from '../../imageknife/RequestOption'
export interface EngineKeyInterface {
// 生成内存缓存
generateMemoryCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean): string
// 生成原图变换后的图片的磁盘缓存
generateTransformedDiskCacheKey(loadSrc: string, size: string, transformed: string, dontAnimate: boolean): string
// 生成原图的磁盘缓存
generateOriginalDiskCacheKey(loadSrc: string): string
}

View File

@ -16,6 +16,7 @@
import { DiskLruCache } from "@ohos/disklrucache"
import { LruCache } from "../cache/LruCache"
import {EngineKeyFactories} from "../cache/key/EngineKeyFactories"
import {EngineKeyInterface} from "../cache/key/EngineKeyInterface"
import {RequestOption} from "../imageknife/RequestOption"
import {AsyncCallback} from "../imageknife/interface/asynccallback"
import {PlaceHolderManager} from "../imageknife/holder/PlaceHolderManager"
@ -60,7 +61,8 @@ export class ImageKnife {
private defaultLifeCycle: IDrawLifeCycle;
// 开发者可配置全局缓存
private engineKeyImpl: EngineKeyInterface;
private constructor(imgCtx) {
this.imageKnifeContext = imgCtx;
@ -85,6 +87,8 @@ export class ImageKnife {
// 通用文件格式识别初始化
this.fileTypeUtil = new FileTypeUtil();
this.engineKeyImpl = new EngineKeyFactories();
}
getMemoryCache(): LruCache<string, any>{
@ -137,6 +141,9 @@ export class ImageKnife {
this.defaultLifeCycle = viewLifeCycle;
}
setEngineKeyImpl(impl:EngineKeyInterface){
this.engineKeyImpl = impl;
}
private static sInstance: ImageKnife;
@ -217,18 +224,45 @@ export class ImageKnife {
let cacheKey;
let transferKey;
let dataKey;
factories = new EngineKeyFactories();
if(this.engineKeyImpl){
factories = this.engineKeyImpl;
}else {
factories = new EngineKeyFactories();
}
// 生成内存缓存key 内存 变换后磁盘
cacheKey = factories.buildCacheKey(request);
let loadKey = '';
if(typeof request.loadSrc == 'string'){
loadKey = request.loadSrc;
}else{
loadKey = JSON.stringify(request.loadSrc);
}
let size = JSON.stringify(request.size);
let transformed = '';
if(request && request.transformations) {
for (let i = 0; i < request.transformations.length; i++) {
if (i == request.transformations.length - 1) {
transformed += request.transformations[i].getName() + "";
} else {
transformed += request.transformations[i].getName() + ",";
}
}
}
let dontAnimateFlag = request.dontAnimateFlag;
cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag);
// 生成磁盘缓存变换后数据key 变换后数据保存在磁盘
transferKey = factories.buildResourceKey(request);
transferKey = factories.generateTransformedDiskCacheKey(loadKey,size,transformed,dontAnimateFlag);
// 生成磁盘缓存源数据key 原始数据保存在磁盘
dataKey = factories.buildDataKey(request);
dataKey = factories.generateOriginalDiskCacheKey(loadKey);
request.generateCacheKey = cacheKey;
request.generateResourceKey = transferKey;

View File

@ -19,7 +19,7 @@ import { RequestOption } from '../imageknife/RequestOption'
import { ImageKnifeData } from '../imageknife/ImageKnifeData'
import { GIFFrame } from '../imageknife/utils/gif/GIFFrame'
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
import {LogUtil} from '../imageknife/utils/LogUtil'
import { LogUtil } from '../imageknife/utils/LogUtil'
@Component
@ -251,6 +251,15 @@ export struct ImageKnifeComponent {
}
// imageknife 第一次启动和数据刷新后重新发送请求
imageKnifeExecute() {
if (this.imageKnifeOption == null || this.imageKnifeOption.loadSrc == null) {
// 如果数据是null或者undefined,清空图片内容和gif循环并且不进入图片请求流程
this.resetGifData()
if (this.canvasHasReady) {
// 如果canvas已经初始化好了清空原有的canvas内容
this.context.clearRect(0, 0, this.currentWidth, this.currentHeight)
}
return
}
this.resetGifData()
let request = new RequestOption();
this.configNecessary(request);
@ -579,7 +588,7 @@ export struct ImageKnifeComponent {
* 绘制直接到第几帧方法由于gif非第一帧数据可能是不全的这里采用逐帧渲染的方式来绘制保证图像的完整性
*/
private drawSeekToFrame(frames: GIFFrame[], context: CanvasRenderingContext2D, compWidth: number, compHeight: number) {
for (let i = 0; i < this.imageKnifeOption.gif; i++) {
for (let i = 0; i < this.imageKnifeOption.gif.seekTo; i++) {
this.drawFrame(frames, i, context, compWidth, compHeight);
}
}
@ -624,6 +633,7 @@ export struct ImageKnifeComponent {
if (index >= frames.length) {
index = 0;
}
// @ts-ignore
this.gifTimerId = setTimeout(this.renderFrames.bind(this, frames, index, context, compWidth, compHeight), delayTime)
}
}

View File

@ -19,12 +19,14 @@ import { Md5 } from '../../cache/Md5'
import { FileUtils } from '../../cache/FileUtils'
import { NetworkDownloadClient } from './NetworkDownloadClient'
import { LoadLocalFileClient } from './LoadLocalFileClient'
import { LoadDataShareFileClient } from './LoadDataShareFileClient'
import loadRequest from '@ohos.request';
// 数据加载器
export class DownloadClient implements IDataFetch {
private networkClient = new NetworkDownloadClient();
private localFileClient = new LoadLocalFileClient();
private dataShareFileClient = new LoadDataShareFileClient();
loadData(request: RequestOption, onCompleteFunction, onErrorFunction) {
if (typeof request.loadSrc == 'string') {
@ -32,6 +34,8 @@ export class DownloadClient implements IDataFetch {
.filesDir) || request.loadSrc.startsWith(globalThis.ImageKnife.getImageKnifeContext().cacheDir)) {
// 本地沙盒
this.localFileClient.loadData(request, onCompleteFunction, onErrorFunction)
} else if (request.loadSrc.startsWith('datashare://')) {
this.dataShareFileClient.loadData(request, onCompleteFunction, onErrorFunction)
} else {
// 网络下载
this.networkClient.loadData(request, onCompleteFunction, onErrorFunction)

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 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 { IDataFetch } from '../networkmanage/IDataFetch'
import { RequestOption } from '../RequestOption'
import fs from '@ohos.file.fs';
export class LoadDataShareFileClient implements IDataFetch {
loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) {
if (typeof request.loadSrc == 'string') {
fs.open(request.loadSrc, fs.OpenMode.READ_ONLY).then((file) => {
let stat = fs.statSync(file.fd);
let buf = new ArrayBuffer(stat.size);
fs.read(file.fd, buf).then((readLen) => {
onComplete(buf);
fs.close(file.fd);
}).catch(err => {
onError('LoadDataShareFileClient fs.read err happend uri=' + request.loadSrc + " err.msg=" + err.message + " err.code=" + err.code)
})
}).catch(err => {
onError('LoadDataShareFileClient fs.open err happend uri=' + request.loadSrc + " err.msg=" + err.message + " err.code=" + err.code)
})
}
}
}

View File

@ -19,8 +19,8 @@ import { Md5 } from '../../cache/Md5'
import { FileUtils } from '../../cache/FileUtils'
import loadRequest from '@ohos.request';
export class LoadLocalFileClient {
loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) {
export class LoadLocalFileClient implements IDataFetch {
loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) {
if (typeof request.loadSrc == 'string') {
let fileBuffer = FileUtils.getInstance().readFilePic(request.loadSrc)
if (fileBuffer == null || fileBuffer.byteLength <= 0) {

View File

@ -38,7 +38,7 @@ export class NetworkDownloadClient {
enableMetered: true,
};
let loadTask = null;
loadRequest.download(globalThis.ImageKnife.getImageKnifeContext(), downloadConfig).then(downloadTask => {
loadRequest.downloadFile(globalThis.ImageKnife.getImageKnifeContext(), downloadConfig).then(downloadTask => {
if (downloadTask) {
loadTask = downloadTask;
@ -114,7 +114,7 @@ export class NetworkDownloadClient {
}
})
.catch((err) => {
onError("下载子系统download错误捕获,error="+err.message);
})
onError("下载子系统download错误捕获,error=" + err.message);
})
}
}

View File

@ -14,7 +14,6 @@
"@ohos/hypium": "1.0.3",
"@ohos/hvigor-ohos-plugin": "1.4.0",
"hypium": "^1.0.0",
"@ohos/hvigor": "1.4.0",
"@ohos/gpu_transform": "^0.1.0"
"@ohos/hvigor": "1.4.0"
}
}