1.新增全局暂停加载和全局重新加载的能力。
Signed-off-by: zhoulisheng <635547767@qq.com>
This commit is contained in:
parent
9708de4ac0
commit
ad2c0ed0a1
|
@ -31,7 +31,26 @@ struct ManyPhotoShowPage {
|
||||||
build() {
|
build() {
|
||||||
Column() {
|
Column() {
|
||||||
|
|
||||||
|
Button('点击暂停加载')
|
||||||
|
.margin({top:10,bottom:5})
|
||||||
|
.onClick(()=>{
|
||||||
|
let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||||
|
if(imageKnife!= undefined){
|
||||||
|
imageKnife.pauseRequests()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Button('点击重新加载')
|
||||||
|
.margin({top:10,bottom:5})
|
||||||
|
.onClick(()=>{
|
||||||
|
let imageKnife = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||||
|
if(imageKnife!= undefined){
|
||||||
|
imageKnife.resumeRequests()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Button('设置磁盘存储为50M')
|
Button('设置磁盘存储为50M')
|
||||||
|
.margin({top:10,bottom:5})
|
||||||
.onClick(()=>{
|
.onClick(()=>{
|
||||||
if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) {
|
if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) {
|
||||||
let disk: DiskLruCache | undefined = (ImageKnifeGlobal.getInstance().getImageKnife())?.getDiskMemoryCache();
|
let disk: DiskLruCache | undefined = (ImageKnifeGlobal.getInstance().getImageKnife())?.getDiskMemoryCache();
|
||||||
|
|
|
@ -15,60 +15,68 @@
|
||||||
|
|
||||||
import { DiskLruCache } from "@ohos/disklrucache"
|
import { DiskLruCache } from "@ohos/disklrucache"
|
||||||
import { LruCache } from "../cache/LruCache"
|
import { LruCache } from "../cache/LruCache"
|
||||||
import {EngineKeyFactories} from "../cache/key/EngineKeyFactories"
|
import { EngineKeyFactories } from "../cache/key/EngineKeyFactories"
|
||||||
import {EngineKeyInterface} from "../cache/key/EngineKeyInterface"
|
import { EngineKeyInterface } from "../cache/key/EngineKeyInterface"
|
||||||
import {RequestOption} from "../imageknife/RequestOption"
|
import { RequestOption } from "../imageknife/RequestOption"
|
||||||
import {AsyncCallback} from "../imageknife/interface/AsyncCallback"
|
import { AsyncCallback } from "../imageknife/interface/AsyncCallback"
|
||||||
import {PlaceHolderManager} from "../imageknife/holder/PlaceHolderManager"
|
import { PlaceHolderManager } from "../imageknife/holder/PlaceHolderManager"
|
||||||
import {RetryHolderManager} from "../imageknife/holder/RetryHolderManager"
|
import { RetryHolderManager } from "../imageknife/holder/RetryHolderManager"
|
||||||
import {ErrorHolderManager} from "../imageknife/holder/ErrorHolderManager"
|
import { ErrorHolderManager } from "../imageknife/holder/ErrorHolderManager"
|
||||||
import {RequestManager} from "../imageknife/requestmanage/RequestManager"
|
import { RequestManager } from "../imageknife/requestmanage/RequestManager"
|
||||||
import {NONE} from "../cache/diskstrategy/enum/NONE"
|
import { NONE } from "../cache/diskstrategy/enum/NONE"
|
||||||
import {FileTypeUtil} from '../imageknife/utils/FileTypeUtil'
|
import { FileTypeUtil } from '../imageknife/utils/FileTypeUtil'
|
||||||
import {DownloadClient} from '../imageknife/networkmanage/DownloadClient'
|
import { DownloadClient } from '../imageknife/networkmanage/DownloadClient'
|
||||||
import {IDataFetch} from '../imageknife/networkmanage/IDataFetch'
|
import { IDataFetch } from '../imageknife/networkmanage/IDataFetch'
|
||||||
import {ParseResClient} from '../imageknife/resourcemanage/ParseResClient'
|
import { ParseResClient } from '../imageknife/resourcemanage/ParseResClient'
|
||||||
import {IResourceFetch} from '../imageknife/resourcemanage/IResourceFetch'
|
import { IResourceFetch } from '../imageknife/resourcemanage/IResourceFetch'
|
||||||
import {ImageKnifeData,ImageKnifeType} from '../imageknife/ImageKnifeData'
|
import { ImageKnifeData, ImageKnifeType } from '../imageknife/ImageKnifeData'
|
||||||
import {ImageKnifeGlobal} from '../imageknife/ImageKnifeGlobal'
|
import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
|
||||||
import image from "@ohos.multimedia.image"
|
import image from "@ohos.multimedia.image"
|
||||||
import {CompressBuilder} from "../imageknife/compress/CompressBuilder"
|
import { CompressBuilder } from "../imageknife/compress/CompressBuilder"
|
||||||
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
|
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
|
||||||
import {LogUtil} from '../imageknife/utils/LogUtil'
|
import { LogUtil } from '../imageknife/utils/LogUtil'
|
||||||
|
import { EasyLinkedHashMap } from './utils/base/EasyLinkedHashMap'
|
||||||
|
import { MethodMutex } from './utils/base/MethodMutex'
|
||||||
import worker from '@ohos.worker'
|
import worker from '@ohos.worker'
|
||||||
import common from '@ohos.app.ability.common'
|
import common from '@ohos.app.ability.common'
|
||||||
|
import HashMap from '@ohos.util.HashMap'
|
||||||
|
import LinkedList from '@ohos.util.LinkedList'
|
||||||
|
|
||||||
export class ImageKnife {
|
export class ImageKnife {
|
||||||
static readonly SEPARATOR: string = '/'
|
static readonly SEPARATOR: string = '/'
|
||||||
|
memoryCache: LruCache<string, ImageKnifeData>;
|
||||||
memoryCache: LruCache<string, ImageKnifeData>;
|
diskMemoryCache: DiskLruCache;
|
||||||
diskMemoryCache: DiskLruCache;
|
dataFetch: IDataFetch;
|
||||||
dataFetch: IDataFetch;
|
resourceFetch: IResourceFetch<ArrayBuffer>;
|
||||||
resourceFetch: IResourceFetch<ArrayBuffer>;
|
filesPath: string = ""; // data/data/包名/files目录
|
||||||
filesPath: string = ""; // data/data/包名/files目录
|
|
||||||
|
|
||||||
|
|
||||||
placeholderCache: string = "placeholderCache"
|
placeholderCache: string = "placeholderCache"
|
||||||
runningRequest: Array<RequestOption>;
|
runningMaps: EasyLinkedHashMap<string, RequestOption>;
|
||||||
pendingRequest: Array<RequestOption>;
|
pendingMaps: EasyLinkedHashMap<string, RequestOption>;
|
||||||
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
|
pausedMaps: EasyLinkedHashMap<string, RequestOption>;
|
||||||
diskCacheFolder: string = "ImageKnifeDiskCache"
|
isPaused: boolean = false;
|
||||||
|
mutex: MethodMutex = new MethodMutex();
|
||||||
|
fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
|
||||||
defaultListener: AsyncCallback<ImageKnifeData> = {
|
diskCacheFolder: string = "ImageKnifeDiskCache"
|
||||||
callback:(err: string, data: ImageKnifeData)=>{return false}
|
defaultListener: AsyncCallback<ImageKnifeData> = {
|
||||||
|
callback: (err: string, data: ImageKnifeData) => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}; // 全局监听器
|
}; // 全局监听器
|
||||||
|
|
||||||
// gifWorker
|
// gifWorker
|
||||||
gifWorker: worker.ThreadWorker|undefined = undefined;
|
gifWorker: worker.ThreadWorker | undefined = undefined;
|
||||||
|
defaultLifeCycle: IDrawLifeCycle | undefined = undefined;
|
||||||
defaultLifeCycle: IDrawLifeCycle|undefined = undefined;
|
|
||||||
|
|
||||||
// 开发者可配置全局缓存
|
// 开发者可配置全局缓存
|
||||||
engineKeyImpl: EngineKeyInterface;
|
engineKeyImpl: EngineKeyInterface;
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
|
|
||||||
|
this.runningMaps = new EasyLinkedHashMap();
|
||||||
|
this.pendingMaps = new EasyLinkedHashMap();
|
||||||
|
this.pausedMaps = new EasyLinkedHashMap();
|
||||||
|
|
||||||
// 构造方法传入size 为保存文件个数
|
// 构造方法传入size 为保存文件个数
|
||||||
this.memoryCache = new LruCache<string, ImageKnifeData>(100);
|
this.memoryCache = new LruCache<string, ImageKnifeData>(100);
|
||||||
|
|
||||||
|
@ -84,22 +92,21 @@ export class ImageKnife {
|
||||||
// 初始化本地 文件保存
|
// 初始化本地 文件保存
|
||||||
this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string;
|
this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string;
|
||||||
|
|
||||||
this.runningRequest = new Array();
|
|
||||||
this.pendingRequest = new Array();
|
|
||||||
|
|
||||||
// 通用文件格式识别初始化
|
// 通用文件格式识别初始化
|
||||||
this.fileTypeUtil = new FileTypeUtil();
|
this.fileTypeUtil = new FileTypeUtil();
|
||||||
|
|
||||||
this.engineKeyImpl = new EngineKeyFactories();
|
this.engineKeyImpl = new EngineKeyFactories();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMemoryCache(): LruCache<string, ImageKnifeData>{
|
getMemoryCache(): LruCache<string, ImageKnifeData> {
|
||||||
return this.memoryCache;
|
return this.memoryCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static with(context:Object): ImageKnifeGlobal{
|
public static with(context: Object): ImageKnifeGlobal {
|
||||||
// 存入hapContext;
|
// 存入hapContext;
|
||||||
let global:ImageKnifeGlobal = ImageKnifeGlobal.getInstance();
|
let global: ImageKnifeGlobal = ImageKnifeGlobal.getInstance();
|
||||||
global.setHapContext(context)
|
global.setHapContext(context)
|
||||||
|
|
||||||
// 初始化ImageKnife
|
// 初始化ImageKnife
|
||||||
|
@ -113,7 +120,7 @@ export class ImageKnife {
|
||||||
return global;
|
return global;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDiskMemoryCache(): DiskLruCache{
|
getDiskMemoryCache(): DiskLruCache {
|
||||||
return this.diskMemoryCache;
|
return this.diskMemoryCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,7 +128,7 @@ export class ImageKnife {
|
||||||
this.diskMemoryCache = diskLruCache;
|
this.diskMemoryCache = diskLruCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
getFileTypeUtil(): FileTypeUtil{
|
getFileTypeUtil(): FileTypeUtil {
|
||||||
return this.fileTypeUtil;
|
return this.fileTypeUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,22 +144,23 @@ export class ImageKnife {
|
||||||
return this.defaultListener;
|
return this.defaultListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
setGifWorker(worker:worker.ThreadWorker){
|
setGifWorker(worker: worker.ThreadWorker) {
|
||||||
this.gifWorker = worker
|
this.gifWorker = worker
|
||||||
}
|
}
|
||||||
getGifWorker(){
|
|
||||||
|
getGifWorker() {
|
||||||
return this.gifWorker;
|
return this.gifWorker;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultLifeCycle(){
|
getDefaultLifeCycle() {
|
||||||
return this.defaultLifeCycle;
|
return this.defaultLifeCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDefaultLifeCycle(viewLifeCycle:IDrawLifeCycle){
|
setDefaultLifeCycle(viewLifeCycle: IDrawLifeCycle) {
|
||||||
this.defaultLifeCycle = viewLifeCycle;
|
this.defaultLifeCycle = viewLifeCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
setEngineKeyImpl(impl:EngineKeyInterface){
|
setEngineKeyImpl(impl: EngineKeyInterface) {
|
||||||
this.engineKeyImpl = impl;
|
this.engineKeyImpl = impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,34 +170,34 @@ export class ImageKnife {
|
||||||
this.defaultListener = newDefaultListener;
|
this.defaultListener = newDefaultListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public compressBuilder(): CompressBuilder{
|
public compressBuilder(): CompressBuilder {
|
||||||
return new CompressBuilder();
|
return new CompressBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 替代原来的LruCache
|
// 替代原来的LruCache
|
||||||
public replaceLruCache(size:number){
|
public replaceLruCache(size: number) {
|
||||||
if(this.memoryCache.map.size() <= 0) {
|
if (this.memoryCache.map.size() <= 0) {
|
||||||
this.memoryCache = new LruCache<string, ImageKnifeData>(size);
|
this.memoryCache = new LruCache<string, ImageKnifeData>(size);
|
||||||
}else{
|
} else {
|
||||||
let newLruCache = new LruCache<string, ImageKnifeData>(size);
|
let newLruCache = new LruCache<string, ImageKnifeData>(size);
|
||||||
this.memoryCache.foreachLruCache( (value:ImageKnifeData, key:string, map:Object)=> {
|
this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => {
|
||||||
newLruCache.put(key, value);
|
newLruCache.put(key, value);
|
||||||
})
|
})
|
||||||
this.memoryCache = newLruCache;
|
this.memoryCache = newLruCache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public replaceDataFetch(fetch:IDataFetch){
|
public replaceDataFetch(fetch: IDataFetch) {
|
||||||
this.dataFetch = fetch;
|
this.dataFetch = fetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 替代原来的DiskLruCache
|
// 替代原来的DiskLruCache
|
||||||
public replaceDiskLruCache(size:number) {
|
public replaceDiskLruCache(size: number) {
|
||||||
if (this.diskMemoryCache.getCacheMap().size() <= 0) {
|
if (this.diskMemoryCache.getCacheMap().size() <= 0) {
|
||||||
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
||||||
} else {
|
} else {
|
||||||
let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size);
|
||||||
this.diskMemoryCache.foreachDiskLruCache( (value:string|ArrayBuffer, key:string, map:Object)=> {
|
this.diskMemoryCache.foreachDiskLruCache((value: string | ArrayBuffer, key: string, map: Object) => {
|
||||||
newDiskLruCache.set(key, value);
|
newDiskLruCache.set(key, value);
|
||||||
})
|
})
|
||||||
this.diskMemoryCache = newDiskLruCache;
|
this.diskMemoryCache = newDiskLruCache;
|
||||||
|
@ -197,17 +205,74 @@ export class ImageKnife {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预加载 resource资源一级缓存,string资源实现二级缓存
|
// 预加载 resource资源一级缓存,string资源实现二级缓存
|
||||||
preload(request: RequestOption):void {
|
preload(request: RequestOption): void {
|
||||||
// 每个request 公共信息补充
|
// 每个request 公共信息补充
|
||||||
request.setFilesPath(this.filesPath);
|
request.setFilesPath(this.filesPath);
|
||||||
|
|
||||||
return this.parseSource(request);
|
return this.parseSource(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 暂停所有请求
|
||||||
|
async pauseRequests(): Promise<void> {
|
||||||
|
await this.mutex.lock(async () => {
|
||||||
|
this.isPaused = true;
|
||||||
|
|
||||||
|
// 将未删除的所有request [run pend] 放入 [pause]
|
||||||
|
this.pausedMaps.clear()
|
||||||
|
console.log('dodo pauseRequests start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||||||
|
|
||||||
|
// 将run存入pause
|
||||||
|
let runNode = this.runningMaps.getHead();
|
||||||
|
while (runNode) {
|
||||||
|
let request = runNode.value;
|
||||||
|
this.pausedMaps.put(request.uuid, request)
|
||||||
|
runNode = runNode.next;
|
||||||
|
}
|
||||||
|
this.runningMaps.clear();
|
||||||
|
console.log('dodo pauseRequests start2 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||||||
|
|
||||||
|
let pendNode = this.pendingMaps.getHead();
|
||||||
|
while (pendNode) {
|
||||||
|
let request = pendNode.value;
|
||||||
|
this.pausedMaps.put(request.uuid, request)
|
||||||
|
pendNode = pendNode.next
|
||||||
|
}
|
||||||
|
this.pendingMaps.clear()
|
||||||
|
console.log('dodo pauseRequests start3 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async resumeRequests(): Promise<void> {
|
||||||
|
await this.mutex.lock(async () => {
|
||||||
|
this.isPaused = false;
|
||||||
|
console.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||||||
|
// 重启了之后需要把paused 里面的所有request重新发送
|
||||||
|
let headNode = this.pausedMaps.getHead();
|
||||||
|
while (headNode) {
|
||||||
|
let request = headNode.value
|
||||||
|
this.loadCacheManager(request)
|
||||||
|
headNode = headNode.next
|
||||||
|
}
|
||||||
|
this.pausedMaps.clear()
|
||||||
|
console.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 请求
|
||||||
|
remove(uuid: string) {
|
||||||
|
if (this.isPaused) {
|
||||||
|
this.pausedMaps.remove(uuid)
|
||||||
|
} else {
|
||||||
|
// TODO 哪些请求可以删除
|
||||||
|
this.pendingMaps.remove(uuid)
|
||||||
|
this.runningMaps.remove(uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 正常加载
|
// 正常加载
|
||||||
call(request: RequestOption):void {
|
call(request: RequestOption): void {
|
||||||
// 添加全局监听
|
// 添加全局监听
|
||||||
if(this.defaultListener) {
|
if (this.defaultListener) {
|
||||||
request.addListener(this.defaultListener)
|
request.addListener(this.defaultListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,28 +296,28 @@ export class ImageKnife {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadResources(request: RequestOption) {
|
loadResources(request: RequestOption) {
|
||||||
let factories:EngineKeyInterface;
|
let factories: EngineKeyInterface;
|
||||||
let cacheKey:string;
|
let cacheKey: string;
|
||||||
let transferKey:string;
|
let transferKey: string;
|
||||||
let dataKey:string;
|
let dataKey: string;
|
||||||
if(this.engineKeyImpl){
|
if (this.engineKeyImpl) {
|
||||||
factories = this.engineKeyImpl;
|
factories = this.engineKeyImpl;
|
||||||
}else {
|
} else {
|
||||||
factories = new EngineKeyFactories();
|
factories = new EngineKeyFactories();
|
||||||
}
|
}
|
||||||
// 生成内存缓存key 内存 变换后磁盘
|
// 生成内存缓存key 内存 变换后磁盘
|
||||||
|
|
||||||
let loadKey = '';
|
let loadKey = '';
|
||||||
if(typeof request.loadSrc == 'string'){
|
if (typeof request.loadSrc == 'string') {
|
||||||
loadKey = request.loadSrc;
|
loadKey = request.loadSrc;
|
||||||
}else{
|
} else {
|
||||||
loadKey = JSON.stringify(request.loadSrc);
|
loadKey = JSON.stringify(request.loadSrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = JSON.stringify(request.size);
|
let size = JSON.stringify(request.size);
|
||||||
|
|
||||||
let transformed = '';
|
let transformed = '';
|
||||||
if(request && request.transformations) {
|
if (request && request.transformations) {
|
||||||
for (let i = 0; i < request.transformations.length; i++) {
|
for (let i = 0; i < request.transformations.length; i++) {
|
||||||
if (i == request.transformations.length - 1) {
|
if (i == request.transformations.length - 1) {
|
||||||
transformed += request.transformations[i].getName() + "";
|
transformed += request.transformations[i].getName() + "";
|
||||||
|
@ -289,120 +354,133 @@ export class ImageKnife {
|
||||||
|
|
||||||
// 删除执行结束的running
|
// 删除执行结束的running
|
||||||
removeRunning(request: RequestOption) {
|
removeRunning(request: RequestOption) {
|
||||||
let index = -1;
|
if (this.isPaused) {
|
||||||
for (let i = 0; i < this.runningRequest.length; i++) {
|
|
||||||
let tempRunning = this.runningRequest[i];
|
} else {
|
||||||
if (this.keyEqual(request, tempRunning)) {
|
this.runningMaps.remove(request.uuid);
|
||||||
// 如果key相同 说明找到执行的request,我们记录下当前request的index位置
|
console.log('dodo runningMaps length =' + this.runningMaps.size())
|
||||||
index = i;
|
let previousRequest = request;
|
||||||
break;
|
this.loadNextPending(previousRequest);
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index >= 0) {
|
|
||||||
let request = this.runningRequest.splice(index, 1)[0];
|
|
||||||
this.loadNextPending(request);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行相同key的pending队列请求
|
// 执行相同key的pending队列请求
|
||||||
keyEqualPendingToRun(index:number){
|
private keyEqualPendingToRun(nextPending: RequestOption) {
|
||||||
let nextPending = this.pendingRequest.splice(index, 1)[0];
|
// let nextPending = this.pendingRequest.splice(index, 1)[0];
|
||||||
this.runningRequest.push(nextPending)
|
// this.runningRequest.push(nextPending)
|
||||||
|
// RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||||||
|
|
||||||
|
this.pendingMaps.remove(nextPending.uuid)
|
||||||
|
this.runningMaps.put(nextPending.uuid, nextPending);
|
||||||
|
|
||||||
RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
searchNextKeyToRun(){
|
private searchNextKeyToRun() {
|
||||||
// 其次则寻找pending中第一个和running不重复的requestOrKey
|
// 其次则寻找pending中第一个和running不重复的requestOrKey
|
||||||
let index2 = -1;
|
let pendingTailNode = this.pendingMaps.getTail();
|
||||||
for (let i = 0; i < this.pendingRequest.length; i++) {
|
while (pendingTailNode) {
|
||||||
let temppending = this.pendingRequest[i];
|
let pendingRequest = pendingTailNode.value;
|
||||||
let hasKeyEqual = false;
|
let hasEqual = false;
|
||||||
for (let j = 0; j < this.runningRequest.length; j++) {
|
let runningTailNode = this.runningMaps.getTail();
|
||||||
let temprunning = this.runningRequest[j];
|
while (runningTailNode) {
|
||||||
if (this.requestOrKeyEqual(temppending, temprunning)) {
|
let runningRequest = runningTailNode.value;
|
||||||
hasKeyEqual = true;
|
if (this.requestOrKeyEqual(pendingRequest, runningRequest)) {
|
||||||
break;
|
hasEqual = true;
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
runningTailNode = runningTailNode.prev;
|
||||||
}
|
}
|
||||||
if (!hasKeyEqual) {
|
|
||||||
index2 = i;
|
if (hasEqual) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
pendingTailNode = pendingTailNode.prev;
|
||||||
}
|
}
|
||||||
if (index2 >= 0) {
|
|
||||||
let nextPending = this.pendingRequest.splice(index2, 1)[0];
|
if (pendingTailNode != null && pendingTailNode.value != null) {
|
||||||
this.runningRequest.push(nextPending)
|
let nextPending = pendingTailNode.value;
|
||||||
|
this.runningMaps.put(nextPending.uuid, nextPending)
|
||||||
|
this.pendingMaps.remove(nextPending.uuid)
|
||||||
RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||||||
} else {
|
|
||||||
// 不执行
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 加载下一个key的请求
|
// 加载下一个key的请求
|
||||||
loadNextPending(request:RequestOption) {
|
private loadNextPending(request: RequestOption) {
|
||||||
// 首先寻找被移除key相同的request
|
let hasEqualRunning = false;
|
||||||
let index = -1;
|
let tailNode = this.pendingMaps.getTail();
|
||||||
for (let i = 0; i < this.pendingRequest.length; i++) {
|
while (tailNode) {
|
||||||
let temppending = this.pendingRequest[i];
|
if (this.requestOrKeyEqual(request, tailNode.value)) {
|
||||||
if (this.requestOrKeyEqual(request, temppending)) {
|
hasEqualRunning = true;
|
||||||
// 如果key相同 说明目前有任务正在执行,我们记录下当前request 放入pendingRunning
|
|
||||||
index = i;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
tailNode = tailNode.prev;
|
||||||
}
|
}
|
||||||
if (index >= 0) {
|
|
||||||
this.keyEqualPendingToRun(index);
|
if (hasEqualRunning) {
|
||||||
|
this.keyEqualPendingToRun(tailNode.value);
|
||||||
} else {
|
} else {
|
||||||
this.searchNextKeyToRun();
|
this.searchNextKeyToRun();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动新线程 去磁盘取 去网络取
|
// 启动新线程 去磁盘取 去网络取
|
||||||
loadCacheManager(request: RequestOption) {
|
private loadCacheManager(request: RequestOption) {
|
||||||
if (this.keyNotEmpty(request)) {
|
if (this.isPaused) {
|
||||||
let hasRunningRequest = false;
|
// 将当前request存入pausedMaps
|
||||||
for (let i = 0; i < this.runningRequest.length; i++) {
|
this.pausedMaps.put(request.uuid, request);
|
||||||
let tempRunning = this.runningRequest[i];
|
} else {
|
||||||
if (this.requestOrKeyEqual(request, tempRunning)) {
|
// 正常逻辑
|
||||||
|
if (this.keyNotEmpty(request)) {
|
||||||
|
let hasRunningRequest = false;
|
||||||
|
// 遍历双向链表 从尾巴到头
|
||||||
|
let tailNode = this.runningMaps.getTail();
|
||||||
|
while (tailNode) {
|
||||||
|
if (this.requestOrKeyEqual(request, tailNode.value)) {
|
||||||
|
hasRunningRequest = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tailNode = tailNode.prev
|
||||||
|
}
|
||||||
|
|
||||||
// 如果requestOrKey相同 说明目前有任务正在执行,我们记录下当前request 放入pendingRunning
|
if (hasRunningRequest) {
|
||||||
hasRunningRequest = true;
|
this.pendingMaps.put(request.uuid, request);
|
||||||
break;
|
|
||||||
|
// this.pendingRequest.push(request);
|
||||||
|
} else {
|
||||||
|
this.runningMaps.put(request.uuid, request)
|
||||||
|
|
||||||
|
// this.runningRequest.push(request);
|
||||||
|
// 不存在相同key的 任务可以并行
|
||||||
|
RequestManager.execute(request, this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasRunningRequest) {
|
else {
|
||||||
this.pendingRequest.push(request);
|
LogUtil.log("key没有生成无法进入存取!")
|
||||||
} else {
|
|
||||||
this.runningRequest.push(request);
|
|
||||||
// 不存在相同key的 任务可以并行
|
|
||||||
RequestManager.execute(request, this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
LogUtil.log("key没有生成无法进入存取!")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private keyNotEmpty(request: RequestOption): boolean{
|
private keyNotEmpty(request: RequestOption): boolean {
|
||||||
if (
|
if (
|
||||||
request.generateCacheKey != null && request.generateCacheKey.length > 0 &&
|
request.generateCacheKey != null && request.generateCacheKey.length > 0 &&
|
||||||
request.generateDataKey != null && request.generateDataKey.length > 0 &&
|
request.generateDataKey != null && request.generateDataKey.length > 0 &&
|
||||||
request.generateResourceKey != null && request.generateResourceKey.length > 0
|
request.generateResourceKey != null && request.generateResourceKey.length > 0
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private keyEqual(request1: RequestOption, request2: RequestOption): boolean{
|
private keyEqual(request1: RequestOption, request2: RequestOption): boolean {
|
||||||
// key 完全相等的情况
|
// key 完全相等的情况
|
||||||
if (
|
if (
|
||||||
request1.generateCacheKey == request2.generateCacheKey &&
|
request1.generateCacheKey == request2.generateCacheKey &&
|
||||||
request1.generateResourceKey == request2.generateResourceKey &&
|
request1.generateResourceKey == request2.generateResourceKey &&
|
||||||
request1.generateDataKey == request2.generateDataKey
|
request1.generateDataKey == request2.generateDataKey
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -411,18 +489,18 @@ export class ImageKnife {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 非严格校验模式,如果所有key相等我们认为一定相等, 如果请求类型是string 网络请求url或者uri相等 我们也认为该请求应该只发送一个即可,后续请求会去缓存或者磁盘读取
|
// 非严格校验模式,如果所有key相等我们认为一定相等, 如果请求类型是string 网络请求url或者uri相等 我们也认为该请求应该只发送一个即可,后续请求会去缓存或者磁盘读取
|
||||||
private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean{
|
private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean {
|
||||||
// key 完全相等的情况
|
// key 完全相等的情况
|
||||||
if (
|
if (
|
||||||
request1.generateCacheKey == request2.generateCacheKey &&
|
request1.generateCacheKey == request2.generateCacheKey &&
|
||||||
request1.generateResourceKey == request2.generateResourceKey &&
|
request1.generateResourceKey == request2.generateResourceKey &&
|
||||||
request1.generateDataKey == request2.generateDataKey
|
request1.generateDataKey == request2.generateDataKey
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果加载的是网络url或者是本地文件uri读取,那么loadSrc相同就认为是同一个请求
|
// 如果加载的是网络url或者是本地文件uri读取,那么loadSrc相同就认为是同一个请求
|
||||||
if(
|
if (
|
||||||
typeof request1.loadSrc == 'string' && typeof request2.loadSrc == 'string' && request1.loadSrc == request2.loadSrc
|
typeof request1.loadSrc == 'string' && typeof request2.loadSrc == 'string' && request1.loadSrc == request2.loadSrc
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -431,7 +509,7 @@ export class ImageKnife {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseSource(request: RequestOption):void {
|
private parseSource(request: RequestOption): void {
|
||||||
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
|
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
|
||||||
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap)
|
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap)
|
||||||
request.loadComplete(imageKnifeData);
|
request.loadComplete(imageKnifeData);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
import { ImageKnifeOption } from '../imageknife/ImageKnifeOption'
|
import { ImageKnifeOption } from '../imageknife/ImageKnifeOption'
|
||||||
import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
|
import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal'
|
||||||
import { TransformType } from '../imageknife/transform/TransformType'
|
import { TransformType } from '../imageknife/transform/TransformType'
|
||||||
import { RequestOption, Size } from '../imageknife/RequestOption'
|
import { DetachFromLayout, RequestOption, Size } from '../imageknife/RequestOption'
|
||||||
import { ImageKnifeData } from '../imageknife/ImageKnifeData'
|
import { ImageKnifeData } from '../imageknife/ImageKnifeData'
|
||||||
import { GIFFrame } from '../imageknife/utils/gif/GIFFrame'
|
import { GIFFrame } from '../imageknife/utils/gif/GIFFrame'
|
||||||
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
|
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
|
||||||
|
@ -82,6 +82,8 @@ export struct ImageKnifeComponent {
|
||||||
private onReadyNext?: (data:ImageKnifeData|number|undefined) => void = undefined
|
private onReadyNext?: (data:ImageKnifeData|number|undefined) => void = undefined
|
||||||
private onReadyNextData:ImageKnifeData|number|undefined = undefined
|
private onReadyNextData:ImageKnifeData|number|undefined = undefined
|
||||||
|
|
||||||
|
private detachFromLayout:DetachFromLayout;
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
Canvas(this.context)
|
Canvas(this.context)
|
||||||
.width('100%')
|
.width('100%')
|
||||||
|
@ -275,6 +277,7 @@ export struct ImageKnifeComponent {
|
||||||
}
|
}
|
||||||
this.resetGifData()
|
this.resetGifData()
|
||||||
let request = new RequestOption();
|
let request = new RequestOption();
|
||||||
|
this.detachFromLayout = request.detachFromLayout;
|
||||||
this.configNecessary(request);
|
this.configNecessary(request);
|
||||||
this.configCacheStrategy(request);
|
this.configCacheStrategy(request);
|
||||||
this.configDisplay(request);
|
this.configDisplay(request);
|
||||||
|
@ -421,7 +424,7 @@ export struct ImageKnifeComponent {
|
||||||
|
|
||||||
drawPlaceholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
drawPlaceholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||||
LogUtil.log('ImageKnifeComponent default drawPlaceholder start!')
|
LogUtil.log('ImageKnifeComponent default drawPlaceholder start!')
|
||||||
|
|
||||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||||
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||||
let scaleType = (typeof imageKnifeOption.placeholderScaleType == 'number') ? imageKnifeOption.placeholderScaleType : ScaleType.FIT_CENTER
|
let scaleType = (typeof imageKnifeOption.placeholderScaleType == 'number') ? imageKnifeOption.placeholderScaleType : ScaleType.FIT_CENTER
|
||||||
|
@ -476,7 +479,7 @@ export struct ImageKnifeComponent {
|
||||||
|
|
||||||
drawThumbSizeMultiplier(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
drawThumbSizeMultiplier(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||||
LogUtil.log('ImageKnifeComponent default drawThumbSizeMultiplier start!')
|
LogUtil.log('ImageKnifeComponent default drawThumbSizeMultiplier start!')
|
||||||
|
|
||||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||||
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||||
let scaleType = (typeof imageKnifeOption.thumbSizeMultiplierScaleType == 'number') ? imageKnifeOption.thumbSizeMultiplierScaleType : ScaleType.FIT_CENTER
|
let scaleType = (typeof imageKnifeOption.thumbSizeMultiplierScaleType == 'number') ? imageKnifeOption.thumbSizeMultiplierScaleType : ScaleType.FIT_CENTER
|
||||||
|
@ -491,7 +494,7 @@ export struct ImageKnifeComponent {
|
||||||
drawMainSource(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
drawMainSource(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||||
LogUtil.log('ImageKnifeComponent default drawMainSource start!')
|
LogUtil.log('ImageKnifeComponent default drawMainSource start!')
|
||||||
if (data.isPixelMap()) {
|
if (data.isPixelMap()) {
|
||||||
|
|
||||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||||
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
|
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
|
||||||
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType)
|
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType)
|
||||||
|
@ -508,7 +511,7 @@ export struct ImageKnifeComponent {
|
||||||
|
|
||||||
drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||||
LogUtil.log('ImageKnifeComponent default drawRetryholder start!')
|
LogUtil.log('ImageKnifeComponent default drawRetryholder start!')
|
||||||
|
|
||||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||||
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||||
let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER
|
let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER
|
||||||
|
@ -522,7 +525,7 @@ export struct ImageKnifeComponent {
|
||||||
|
|
||||||
drawErrorholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
drawErrorholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||||
LogUtil.log('ImageKnifeComponent default drawErrorholder start!')
|
LogUtil.log('ImageKnifeComponent default drawErrorholder start!')
|
||||||
|
|
||||||
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => {
|
||||||
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
LogUtil.log('ImageKnifeComponent imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
|
||||||
let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER
|
let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER
|
||||||
|
@ -584,6 +587,10 @@ export struct ImageKnifeComponent {
|
||||||
|
|
||||||
aboutToDisappear() {
|
aboutToDisappear() {
|
||||||
LogUtil.log('ImageKnifeComponent aboutToDisappear happened!')
|
LogUtil.log('ImageKnifeComponent aboutToDisappear happened!')
|
||||||
|
if(this.detachFromLayout){
|
||||||
|
this.detachFromLayout.detach();
|
||||||
|
}
|
||||||
|
|
||||||
this.resetGifData();
|
this.resetGifData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,7 +608,7 @@ export struct ImageKnifeComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawLifeCycleHasConsumed<K, T>( methodName: string,
|
private drawLifeCycleHasConsumed<K, T>( methodName: string,
|
||||||
context: CanvasRenderingContext2D, data: K, imageKnifeOption: T, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void,drawLifeCycle?: IDrawLifeCycle
|
context: CanvasRenderingContext2D, data: K, imageKnifeOption: T, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void,drawLifeCycle?: IDrawLifeCycle
|
||||||
):boolean {
|
):boolean {
|
||||||
if (drawLifeCycle && (drawLifeCycle as Record<string,Function>)[methodName]) {
|
if (drawLifeCycle && (drawLifeCycle as Record<string,Function>)[methodName]) {
|
||||||
return (drawLifeCycle as Record<string,Function>)[methodName](context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
|
return (drawLifeCycle as Record<string,Function>)[methodName](context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
|
||||||
|
@ -677,7 +684,7 @@ export struct ImageKnifeComponent {
|
||||||
// 理论上该帧在屏幕上保留的时间
|
// 理论上该帧在屏幕上保留的时间
|
||||||
let stayTime:number= 0
|
let stayTime:number= 0
|
||||||
if(this.renderFrames_frames != undefined) {
|
if(this.renderFrames_frames != undefined) {
|
||||||
stayTime = this.renderFrames_frames[this.renderFrames_index].delay;
|
stayTime = this.renderFrames_frames[this.renderFrames_index].delay;
|
||||||
}
|
}
|
||||||
if (this.imageKnifeOption.gif && this.imageKnifeOption.gif.speedFactory) {
|
if (this.imageKnifeOption.gif && this.imageKnifeOption.gif.speedFactory) {
|
||||||
stayTime = stayTime / (this.imageKnifeOption.gif?.speedFactory * 1.0);
|
stayTime = stayTime / (this.imageKnifeOption.gif?.speedFactory * 1.0);
|
||||||
|
@ -706,9 +713,9 @@ export struct ImageKnifeComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawFrame(frames: GIFFrame[]|undefined, index: number, context: CanvasRenderingContext2D|undefined, compWidth: number, compHeight: number) {
|
private drawFrame(frames: GIFFrame[]|undefined, index: number, context: CanvasRenderingContext2D|undefined, compWidth: number, compHeight: number) {
|
||||||
if(frames == undefined){
|
if(frames == undefined){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// get current frame
|
// get current frame
|
||||||
let frame = frames[index];
|
let frame = frames[index];
|
||||||
if (!frame || !context) {
|
if (!frame || !context) {
|
||||||
|
@ -848,9 +855,9 @@ export class ScaleTypeHelper {
|
||||||
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 1.0);
|
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 1.0);
|
||||||
let dw:number = imageWidth;
|
let dw:number = imageWidth;
|
||||||
let dh:number = imageHeight;
|
let dh:number = imageHeight;
|
||||||
if(source!= undefined) {
|
if(source!= undefined) {
|
||||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static drawFitCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
static drawFitCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
||||||
|
@ -859,9 +866,9 @@ export class ScaleTypeHelper {
|
||||||
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 2.0);
|
let dy:number = (compHeight - imageHeight * minScale) / (minScale * 2.0);
|
||||||
let dw:number = imageWidth;
|
let dw:number = imageWidth;
|
||||||
let dh:number = imageHeight;
|
let dh:number = imageHeight;
|
||||||
if(source!= undefined) {
|
if(source!= undefined) {
|
||||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static drawCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
static drawCenter(context: CanvasRenderingContext2D, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
||||||
|
@ -869,9 +876,9 @@ export class ScaleTypeHelper {
|
||||||
let dy:number = (compHeight - imageHeight) / 2.0;
|
let dy:number = (compHeight - imageHeight) / 2.0;
|
||||||
let dw:number = imageWidth;
|
let dw:number = imageWidth;
|
||||||
let dh:number = imageHeight;
|
let dh:number = imageHeight;
|
||||||
if(source!= undefined) {
|
if(source!= undefined) {
|
||||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static drawCenterCrop(context: CanvasRenderingContext2D, source: PixelMap | undefined, maxScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
static drawCenterCrop(context: CanvasRenderingContext2D, source: PixelMap | undefined, maxScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
||||||
|
@ -880,9 +887,9 @@ export class ScaleTypeHelper {
|
||||||
let dy:number = (compHeight - imageHeight * maxScale) / (maxScale * 2.0);
|
let dy:number = (compHeight - imageHeight * maxScale) / (maxScale * 2.0);
|
||||||
let dw:number = imageWidth;
|
let dw:number = imageWidth;
|
||||||
let dh:number = imageHeight;
|
let dh:number = imageHeight;
|
||||||
if(source!= undefined) {
|
if(source!= undefined) {
|
||||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static drawFitXY(context: CanvasRenderingContext2D, source: PixelMap | undefined, scaleW: number, scaleH: number, imageWidth: number, imageHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
static drawFitXY(context: CanvasRenderingContext2D, source: PixelMap | undefined, scaleW: number, scaleH: number, imageWidth: number, imageHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
||||||
|
@ -891,9 +898,9 @@ export class ScaleTypeHelper {
|
||||||
let dy:number = 0;
|
let dy:number = 0;
|
||||||
let dw:number = imageWidth;
|
let dw:number = imageWidth;
|
||||||
let dh:number = imageHeight;
|
let dh:number = imageHeight;
|
||||||
if(source!= undefined) {
|
if(source!= undefined) {
|
||||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static drawCenterInside(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
static drawCenterInside(context: CanvasRenderingContext2D, source: PixelMap | undefined, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?: number, imageOffsetY?: number) {
|
||||||
|
@ -912,7 +919,7 @@ export class ScaleTypeHelper {
|
||||||
if(source!= undefined) {
|
if(source!= undefined) {
|
||||||
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
context.drawImage(source, dx + (imageOffsetX != undefined ? imageOffsetX : 0), dy + (imageOffsetY != undefined ? imageOffsetY : 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,12 @@ export interface Size {
|
||||||
width: number,
|
width: number,
|
||||||
height: number
|
height: number
|
||||||
}
|
}
|
||||||
|
export interface DetachFromLayout{
|
||||||
|
detach:()=>void
|
||||||
|
}
|
||||||
|
|
||||||
export class RequestOption {
|
export class RequestOption {
|
||||||
|
uuid:string ='' // 唯一标识
|
||||||
loadSrc: string | PixelMap | Resource = '';
|
loadSrc: string | PixelMap | Resource = '';
|
||||||
strategy: DiskStrategy = new AUTOMATIC();
|
strategy: DiskStrategy = new AUTOMATIC();
|
||||||
dontAnimateFlag = false;
|
dontAnimateFlag = false;
|
||||||
|
@ -117,11 +122,30 @@ export class RequestOption {
|
||||||
// 缩略图展示
|
// 缩略图展示
|
||||||
loadThumbnailReady = false;
|
loadThumbnailReady = false;
|
||||||
|
|
||||||
|
detachFromLayout:DetachFromLayout = {
|
||||||
|
detach: ()=>{
|
||||||
|
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||||
|
if(imageKnife != undefined) {
|
||||||
|
imageKnife.remove(this.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// 初始化全局监听
|
// 初始化全局监听
|
||||||
this.requestListeners = new Array();
|
this.requestListeners = new Array();
|
||||||
|
// 初始化唯一标识,可以用这个标识找到ImageKnife中的对象
|
||||||
|
this.uuid = this.generateUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateUUID(): string {
|
||||||
|
let d = new Date().getTime();
|
||||||
|
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
|
const r = (d + Math.random() * 16) % 16 | 0;
|
||||||
|
d = Math.floor(d / 16);
|
||||||
|
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||||
|
});
|
||||||
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,8 +173,6 @@ export class RequestOption {
|
||||||
this.cachesPath = path;
|
this.cachesPath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
load(src: string | PixelMap | Resource) {
|
load(src: string | PixelMap | Resource) {
|
||||||
this.loadSrc = src;
|
this.loadSrc = src;
|
||||||
return this;
|
return this;
|
||||||
|
@ -489,15 +511,15 @@ export class RequestOption {
|
||||||
// 非落盘情况,直接进行寻找下一个加载
|
// 非落盘情况,直接进行寻找下一个加载
|
||||||
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||||
if(imageKnife != undefined) {
|
if(imageKnife != undefined) {
|
||||||
imageKnife.removeRunning(this);
|
imageKnife.removeRunning(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载成功之后
|
// // 加载成功之后
|
||||||
let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
// let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
|
||||||
if(imageKnife != undefined) {
|
// if(imageKnife != undefined) {
|
||||||
imageKnife.removeRunning(this);
|
// imageKnife.removeRunning(this);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图片文件落盘之后会自动去寻找下一个数据加载
|
// 图片文件落盘之后会自动去寻找下一个数据加载
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
|
|
||||||
import { IDataFetch } from '../networkmanage/IDataFetch'
|
import { IDataFetch } from '../networkmanage/IDataFetch'
|
||||||
import { RequestOption } from '../RequestOption'
|
import { RequestOption } from '../RequestOption'
|
||||||
import { NetworkDownloadClient } from './NetworkDownloadClient'
|
|
||||||
|
import { HttpDownloadClient } from './HttpDownloadClient'
|
||||||
import { LoadLocalFileClient } from './LoadLocalFileClient'
|
import { LoadLocalFileClient } from './LoadLocalFileClient'
|
||||||
import { LoadDataShareFileClient } from './LoadDataShareFileClient'
|
import { LoadDataShareFileClient } from './LoadDataShareFileClient'
|
||||||
import loadRequest from '@ohos.request';
|
import loadRequest from '@ohos.request';
|
||||||
|
@ -24,7 +25,7 @@ import common from '@ohos.app.ability.common'
|
||||||
|
|
||||||
// 数据加载器
|
// 数据加载器
|
||||||
export class DownloadClient implements IDataFetch {
|
export class DownloadClient implements IDataFetch {
|
||||||
private networkClient = new NetworkDownloadClient();
|
private httpDownloadClient = new HttpDownloadClient();
|
||||||
private localFileClient = new LoadLocalFileClient();
|
private localFileClient = new LoadLocalFileClient();
|
||||||
private dataShareFileClient = new LoadDataShareFileClient();
|
private dataShareFileClient = new LoadDataShareFileClient();
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ export class DownloadClient implements IDataFetch {
|
||||||
this.dataShareFileClient.loadData(request, onCompleteFunction, onErrorFunction)
|
this.dataShareFileClient.loadData(request, onCompleteFunction, onErrorFunction)
|
||||||
} else {
|
} else {
|
||||||
// 网络下载
|
// 网络下载
|
||||||
this.networkClient.loadData(request, onCompleteFunction, onErrorFunction)
|
this.httpDownloadClient.loadData(request, onCompleteFunction, onErrorFunction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5'
|
||||||
|
import { FileUtils } from '../../cache/FileUtils'
|
||||||
|
import loadRequest from '@ohos.request';
|
||||||
|
import { LogUtil } from '../utils/LogUtil'
|
||||||
|
import { ImageKnifeGlobal } from '../ImageKnifeGlobal'
|
||||||
|
import common from '@ohos.app.ability.common'
|
||||||
|
import { BusinessError } from '@ohos.base'
|
||||||
|
import http from '@ohos.net.http'
|
||||||
|
// 数据加载器
|
||||||
|
class RequestData{
|
||||||
|
receiveSize: number = 2000
|
||||||
|
totalSize: number = 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HttpDownloadClient implements IDataFetch {
|
||||||
|
loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) {
|
||||||
|
try {
|
||||||
|
let httpRequest = http.createHttp()
|
||||||
|
let arrayBuffers = new Array();
|
||||||
|
httpRequest.on('headersReceive', (header: Object) => {
|
||||||
|
// 跟服务器连接成功准备下载
|
||||||
|
if (request.progressFunc) {
|
||||||
|
// 进度条为0
|
||||||
|
request.progressFunc.asyncSuccess(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
httpRequest.on('dataReceive', (data: ArrayBuffer) => {
|
||||||
|
// 下载数据流多次返回
|
||||||
|
arrayBuffers.push(data);
|
||||||
|
})
|
||||||
|
|
||||||
|
httpRequest.on('dataReceiveProgress', (data: RequestData) => {
|
||||||
|
// 下载进度
|
||||||
|
let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100)
|
||||||
|
if (request.progressFunc) {
|
||||||
|
request.progressFunc.asyncSuccess(percent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
httpRequest.on('dataEnd', () => {
|
||||||
|
// 下载完毕
|
||||||
|
let combineArray = this.combineArrayBuffers(arrayBuffers);
|
||||||
|
onComplete(combineArray)
|
||||||
|
})
|
||||||
|
|
||||||
|
httpRequest.requestInStream(
|
||||||
|
request.loadSrc as string,
|
||||||
|
{
|
||||||
|
method: http.RequestMethod.GET,
|
||||||
|
expectDataType: http.HttpDataType.ARRAY_BUFFER,
|
||||||
|
connectTimeout: 60000, // 可选 默认60000ms
|
||||||
|
readTimeout: 0, //可选, 默认为60000ms
|
||||||
|
usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定
|
||||||
|
},
|
||||||
|
(err: BusinessError, data: number) => {
|
||||||
|
if (!err && data == 200) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
httpRequest.off('headersReceive');
|
||||||
|
httpRequest.off('dataReceive');
|
||||||
|
httpRequest.off('dataReceiveProgress');
|
||||||
|
httpRequest.off('dataEnd');
|
||||||
|
httpRequest.destroy()
|
||||||
|
onError('HttpDownloadClient err message =' + err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
onError('HttpDownloadClient catch err request uuid ='+request.uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
combineArrayBuffers(arrayBuffers: ArrayBuffer[]): ArrayBuffer {
|
||||||
|
// 计算多个ArrayBuffer的总字节大小
|
||||||
|
let totalByteLength = 0;
|
||||||
|
for (const arrayBuffer of arrayBuffers) {
|
||||||
|
totalByteLength += arrayBuffer.byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个新的ArrayBuffer
|
||||||
|
const combinedArrayBuffer = new ArrayBuffer(totalByteLength);
|
||||||
|
|
||||||
|
// 创建一个Uint8Array来操作新的ArrayBuffer
|
||||||
|
const combinedUint8Array = new Uint8Array(combinedArrayBuffer);
|
||||||
|
|
||||||
|
// 依次复制每个ArrayBuffer的内容到新的ArrayBuffer中
|
||||||
|
let offset = 0;
|
||||||
|
for (const arrayBuffer of arrayBuffers) {
|
||||||
|
const sourceUint8Array = new Uint8Array(arrayBuffer);
|
||||||
|
combinedUint8Array.set(sourceUint8Array, offset);
|
||||||
|
offset += sourceUint8Array.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedArrayBuffer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -292,6 +292,8 @@ export class RequestManager {
|
||||||
// 处理磁盘加载 网络加载
|
// 处理磁盘加载 网络加载
|
||||||
this.runWrapped(this.options, this.mRunReason, onComplete, onError)
|
this.runWrapped(this.options, this.mRunReason, onComplete, onError)
|
||||||
} else {
|
} else {
|
||||||
|
// 需要清理状态
|
||||||
|
cache.waitSaveDisk = false;
|
||||||
onComplete(cache);
|
onComplete(cache);
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -12,143 +12,80 @@
|
||||||
* 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 {LogUtil} from '../../imageknife/utils/LogUtil'
|
import { LogUtil } from '../../imageknife/utils/LogUtil'
|
||||||
|
|
||||||
|
// jpg = 'jpg,0,FFD8',
|
||||||
|
// png = 'png,0,89504E470D0A1A0A',
|
||||||
|
// bmp = 'bmp,0,424D',
|
||||||
|
// gif = 'gif,0,474946383961',
|
||||||
|
// svg = 'svg,0,3C3F786D6C',
|
||||||
|
// webp = 'webp,0,52494646',
|
||||||
|
// tiff = 'tiff,0,492049|49492A00|4D4D002A|4D4D002B'
|
||||||
|
|
||||||
export class FileTypeUtil {
|
export class FileTypeUtil {
|
||||||
private map:Map<string,string> = new Map<string,string>();
|
private fileSignatureMap: Record<string, Array<Uint8Array>> = {
|
||||||
private READ_MIN_LENGTH:number = 0;
|
// 添加文件类型和对应的文件头部特征
|
||||||
private SUPPORT_FORMATS = [
|
'jpg': [new Uint8Array([0xFF, 0xD8])],
|
||||||
PhotoFormat.jpg,
|
'png': [new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])],
|
||||||
PhotoFormat.png,
|
'gif': [new Uint8Array([0x47, 0x49, 0x46, 0x38, 0x39, 0x61])],
|
||||||
PhotoFormat.tiff,
|
'bmp': [new Uint8Array([0x42, 0x4D])],
|
||||||
PhotoFormat.bmp,
|
'svg': [new Uint8Array([0x3C, 0x3F, 0x78, 0x6D, 0x6C])],
|
||||||
PhotoFormat.webp,
|
'webp': [new Uint8Array([0x52, 0x49, 0x46, 0x46])],
|
||||||
PhotoFormat.svg,
|
'tiff': [new Uint8Array([0x49, 0x20, 0x49]), new Uint8Array([0x49, 0x49, 0x2A, 0x00]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2A]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2B])],
|
||||||
PhotoFormat.gif
|
// 添加更多的文件类型和特征
|
||||||
]
|
};
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initImageType();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDataViewAt(dataView:DataView,index:number): string{
|
isImage(arraybuffer: ArrayBuffer) {
|
||||||
return this.dec2Hex(dataView.getUint8(index))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getFileType(arraybuffer: ArrayBuffer):string {
|
|
||||||
let fileType:string = '';
|
|
||||||
if (arraybuffer == null || arraybuffer == undefined || arraybuffer.byteLength <= this.READ_MIN_LENGTH) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
let dataView = new DataView(arraybuffer);
|
|
||||||
LogUtil.log('dataView +'+this.getDataViewAt(dataView,0)+this.getDataViewAt(dataView,1))
|
|
||||||
let entries: IterableIterator<Object[]> = this.map.entries();
|
|
||||||
for (let i = 0; i < this.map.size; i++) {
|
|
||||||
let entry:Object[] = entries.next().value;
|
|
||||||
let key = entry[0] as string
|
|
||||||
let value = entry[1] as string
|
|
||||||
let keySplit = key.split(',')
|
|
||||||
if(keySplit.length == 2){
|
|
||||||
let offset = Number(keySplit[0])
|
|
||||||
let magicStringLength = keySplit[1].length;
|
|
||||||
let readLength = magicStringLength/2;
|
|
||||||
let start = 0;
|
|
||||||
let fileMagic = ''
|
|
||||||
while(start< readLength){
|
|
||||||
fileMagic+=this.getDataViewAt(dataView,offset+start)
|
|
||||||
start++;
|
|
||||||
}
|
|
||||||
if(fileMagic == keySplit[1]){
|
|
||||||
LogUtil.log('匹配到了 fileType='+value)
|
|
||||||
fileType = value
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fileType;
|
|
||||||
}
|
|
||||||
|
|
||||||
dec2Hex(uint8: number) {
|
|
||||||
let hex = uint8.toString(16);
|
|
||||||
if (hex.length <= 1) {
|
|
||||||
hex = "0" + hex;
|
|
||||||
}
|
|
||||||
return hex.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
private initImageType() {
|
|
||||||
if(!this.map){
|
|
||||||
this.map = new Map()
|
|
||||||
}
|
|
||||||
this.SUPPORT_FORMATS.forEach((value,index,arrs)=>{
|
|
||||||
let values = value.split(',')
|
|
||||||
if(values.length == 3){
|
|
||||||
let magicSplits = values[2].split("|")
|
|
||||||
if(magicSplits.length == 1){
|
|
||||||
this.map.set(values[1]+','+values[2],values[0])
|
|
||||||
}else if(magicSplits.length > 1){
|
|
||||||
for(let i=0; i<magicSplits.length; i++){
|
|
||||||
let magicStr = magicSplits[i];
|
|
||||||
this.map.set(values[1]+','+magicStr,values[0])
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
// 文件魔数不存在,不处理
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.initReadMinLength();
|
|
||||||
|
|
||||||
this.printMapContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private printMapContent(){
|
|
||||||
|
|
||||||
let entries: IterableIterator<Object[]> = this.map.entries();
|
|
||||||
for (let i = 0; i < this.map.size; i++) {
|
|
||||||
let entry:Object[] = entries.next().value;
|
|
||||||
let key = entry[0] as string
|
|
||||||
let value = entry[1] as string
|
|
||||||
LogUtil.log('key='+key+'---value='+value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private initReadMinLength(){
|
|
||||||
let max = 0;
|
|
||||||
this.map.forEach((value,key,map)=>{
|
|
||||||
let keySplit = key.split(',');
|
|
||||||
if(keySplit.length == 2){
|
|
||||||
let offset = Number(keySplit[0])
|
|
||||||
let magicStringLength = keySplit[1].length;
|
|
||||||
let tempMax = offset + magicStringLength/2;
|
|
||||||
if(tempMax > max){
|
|
||||||
max = tempMax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.READ_MIN_LENGTH =max;
|
|
||||||
}
|
|
||||||
|
|
||||||
isImage(arraybuffer:ArrayBuffer){
|
|
||||||
let value = this.getFileType(arraybuffer);
|
let value = this.getFileType(arraybuffer);
|
||||||
if(
|
if (
|
||||||
value == SupportFormat.jpg ||
|
value == SupportFormat.jpg ||
|
||||||
value == SupportFormat.png ||
|
value == SupportFormat.png ||
|
||||||
value == SupportFormat.tiff ||
|
value == SupportFormat.tiff ||
|
||||||
value == SupportFormat.webp ||
|
value == SupportFormat.webp ||
|
||||||
value == SupportFormat.bmp ||
|
value == SupportFormat.bmp ||
|
||||||
value == SupportFormat.gif ||
|
value == SupportFormat.gif ||
|
||||||
value == SupportFormat.svg
|
value == SupportFormat.svg
|
||||||
){
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFileType(file: ArrayBuffer): string | null {
|
||||||
|
const fileData = new Uint8Array(file);
|
||||||
|
for (const fileType in this.fileSignatureMap) {
|
||||||
|
const bufferList = this.fileSignatureMap[fileType];
|
||||||
|
for (let i = 0; i < bufferList.length; i++) {
|
||||||
|
let signature = bufferList[i];
|
||||||
|
if(this.matchesSignature(fileData,signature)){
|
||||||
|
return fileType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // 若无法识别文件类型,返回null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
matchesSignature(fileData: Uint8Array, signature: Uint8Array): boolean {
|
||||||
|
if (fileData.length < signature.length) {
|
||||||
|
return false; // 文件长度不足,无法匹配魔数
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < signature.length; i++) {
|
||||||
|
if (fileData[i] !== signature[i]) {
|
||||||
|
return false; // 魔数不匹配
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true; // 文件头部魔数匹配
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export enum PhotoFormat {
|
export enum PhotoFormat {
|
||||||
jpg = 'jpg,0,FFD8',
|
jpg = 'jpg,0,FFD8',
|
||||||
png = 'png,0,89504E470D0A1A0A',
|
png = 'png,0,89504E470D0A1A0A',
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class Node<K, V> {
|
||||||
|
key: K;
|
||||||
|
value: V;
|
||||||
|
next: Node<K, V> | null;
|
||||||
|
prev: Node<K, V> | null;
|
||||||
|
|
||||||
|
constructor(key: K, value: V) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
this.next = null;
|
||||||
|
this.prev = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 链表的tail是最近访问,head是最晚访问,对顺序有要求需要访问tail
|
||||||
|
export class EasyLinkedHashMap<K, V> {
|
||||||
|
private map: Map<K, Node<K, V>>; // 存储键值对的哈希映射
|
||||||
|
private head: Node<K, V> | null; // 链表头节点
|
||||||
|
private tail: Node<K, V> | null; // 链表尾节点
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.map = new Map<K, Node<K, V>>();
|
||||||
|
this.head = null;
|
||||||
|
this.tail = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加键值对到映射中,并在链表尾部添加新节点
|
||||||
|
put(key: K, value: V) {
|
||||||
|
if (this.map.has(key)) {
|
||||||
|
// 如果键已存在,则更新对应的值
|
||||||
|
const node = this.map.get(key)!;
|
||||||
|
node.value = value;
|
||||||
|
this.moveToTail(node);
|
||||||
|
} else {
|
||||||
|
// 创建新节点并加入链表尾部
|
||||||
|
const newNode = new Node(key, value);
|
||||||
|
this.map.set(key, newNode);
|
||||||
|
if (this.tail) {
|
||||||
|
this.tail.next = newNode;
|
||||||
|
newNode.prev = this.tail;
|
||||||
|
this.tail = newNode;
|
||||||
|
} else {
|
||||||
|
this.head = newNode;
|
||||||
|
this.tail = newNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从映射中移除键值对,并从链表中删除对应节点
|
||||||
|
remove(key: K) {
|
||||||
|
if (this.map.has(key)) {
|
||||||
|
const node = this.map.get(key)!;
|
||||||
|
this.map.delete(key);
|
||||||
|
this.removeNode(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取键对应的值,并将对应节点移到链表尾部(表示最近访问)
|
||||||
|
get(key: K): V | undefined {
|
||||||
|
if (this.map.has(key)) {
|
||||||
|
const node = this.map.get(key)!;
|
||||||
|
this.moveToTail(node);
|
||||||
|
return node.value;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将节点移动到链表尾部
|
||||||
|
private moveToTail(node: Node<K, V>) {
|
||||||
|
if (node === this.tail) {
|
||||||
|
return; // 节点已在链表尾部,无需移动
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node === this.head) {
|
||||||
|
this.head = node.next;
|
||||||
|
} else {
|
||||||
|
node.prev!.next = node.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.next!.prev = node.prev;
|
||||||
|
this.tail!.next = node;
|
||||||
|
node.prev = this.tail;
|
||||||
|
node.next = null;
|
||||||
|
this.tail = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从链表中删除节点
|
||||||
|
private removeNode(node: Node<K, V>) {
|
||||||
|
if (node === this.head) {
|
||||||
|
this.head = node.next;
|
||||||
|
} else {
|
||||||
|
node.prev!.next = node.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node === this.tail) {
|
||||||
|
this.tail = node.prev;
|
||||||
|
} else {
|
||||||
|
node.next!.prev = node.prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回映射中的键值对数量
|
||||||
|
size(): number {
|
||||||
|
return this.map.size;
|
||||||
|
}
|
||||||
|
// 返回顺序双向链表
|
||||||
|
getTail(){
|
||||||
|
return this.tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHead(){
|
||||||
|
return this.head;
|
||||||
|
}
|
||||||
|
clear(){
|
||||||
|
this.map.clear()
|
||||||
|
this.head = null;
|
||||||
|
this.tail=null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLinkedHashMap(){
|
||||||
|
// 创建一个新的 LinkedHashMap 实例
|
||||||
|
let linkedHashMap = new EasyLinkedHashMap<string, number>();
|
||||||
|
|
||||||
|
// 添加键值对,并验证插入是否正确
|
||||||
|
linkedHashMap.put('key1', 1);
|
||||||
|
linkedHashMap.put('key2', 2);
|
||||||
|
linkedHashMap.put('key3', 3);
|
||||||
|
|
||||||
|
console.log('dodo '+linkedHashMap.get('key1')); // 1
|
||||||
|
console.log('dodo '+linkedHashMap.get('key2')); // 2
|
||||||
|
console.log('dodo '+linkedHashMap.get('key3')); // 3
|
||||||
|
|
||||||
|
// 验证键值对更新是否正确
|
||||||
|
linkedHashMap.put('key2', 20);
|
||||||
|
console.log('dodo '+linkedHashMap.get('key2')); // 20
|
||||||
|
|
||||||
|
// 验证移除键值对是否正确
|
||||||
|
linkedHashMap.remove('key1');
|
||||||
|
console.log('dodo '+linkedHashMap.get('key1')); // undefined
|
||||||
|
console.log('dodo '+linkedHashMap.size()); // 2
|
||||||
|
|
||||||
|
// 添加更多键值对
|
||||||
|
linkedHashMap.put('key4', 4);
|
||||||
|
linkedHashMap.put('key5', 5);
|
||||||
|
linkedHashMap.put('key6', 6);
|
||||||
|
|
||||||
|
console.log('dodo '+linkedHashMap.size()); // 5
|
||||||
|
|
||||||
|
// 验证访问顺序是否正确,最近访问的键值对应该在链表尾部
|
||||||
|
console.log('dodo '+linkedHashMap.get('key2')); // 20
|
||||||
|
console.log('dodo '+linkedHashMap.get('key3')); // 3
|
||||||
|
console.log('dodo '+linkedHashMap.get('key4')); // 4
|
||||||
|
|
||||||
|
// 添加更多键值对,超出容量限制(假设容量限制为 5)
|
||||||
|
linkedHashMap.put('key7', 7);
|
||||||
|
linkedHashMap.put('key8', 8);
|
||||||
|
linkedHashMap.put('key9', 9);
|
||||||
|
|
||||||
|
console.log('dodo '+linkedHashMap.size()); // 5
|
||||||
|
console.log('dodo '+linkedHashMap.get('key1')); // undefined,因为已经超过容量限制,最早访问的键值对被移除
|
||||||
|
|
||||||
|
// 验证移除最近访问的键值对后,访问顺序是否正确
|
||||||
|
linkedHashMap.remove('key4');
|
||||||
|
console.log('dodo '+linkedHashMap.get('key4')); // undefined
|
||||||
|
console.log('dodo '+linkedHashMap.get('key2')); // 20
|
||||||
|
console.log('dodo '+linkedHashMap.get('key3')); // 3
|
||||||
|
|
||||||
|
// 清空 LinkedHashMap
|
||||||
|
linkedHashMap.remove('key2');
|
||||||
|
linkedHashMap.remove('key3');
|
||||||
|
linkedHashMap.remove('key5');
|
||||||
|
linkedHashMap.remove('key6');
|
||||||
|
linkedHashMap.remove('key7');
|
||||||
|
linkedHashMap.remove('key8');
|
||||||
|
linkedHashMap.remove('key9');
|
||||||
|
|
||||||
|
console.log('dodo '+linkedHashMap.size()); // 0
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MethodMutex {
|
||||||
|
private mutex: Promise<void>;
|
||||||
|
private release: () => void;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.mutex = Promise.resolve();
|
||||||
|
this.release = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
async lock<T>(fn: () => Promise<T>): Promise<T> {
|
||||||
|
await this.mutex;
|
||||||
|
let result: T;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.mutex = new Promise<void>((resolve) => {
|
||||||
|
this.release = resolve;
|
||||||
|
});
|
||||||
|
result = await fn();
|
||||||
|
} finally {
|
||||||
|
this.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue