Skip to content

Commit d49f69a

Browse files
committed
feat(core): maximum cache size and age
closes #14
1 parent 26e453b commit d49f69a

File tree

3 files changed

+111
-19
lines changed

3 files changed

+111
-19
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@
3232
"@angular/compiler-cli": "2.2.1",
3333
"@angular/core": "2.2.1",
3434
"@angular/platform-browser": "2.2.1",
35+
"@types/lodash": "^4.14.50",
3536
"conventional-changelog-cli": "^1.2.0",
3637
"ionic-native": "^2.4.1",
3738
"rxjs": "5.0.0-beta.12",
3839
"typescript": "2.0.9",
3940
"zone.js": "0.6.26"
41+
},
42+
"dependencies": {
43+
"lodash": "^4.17.4"
4044
}
4145
}

src/providers/image-loader-config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export class ImageLoaderConfig {
2323

2424
concurrency: number = 5;
2525

26+
maxCacheSize: number = -1;
27+
28+
maxCacheAge: number = -1;
29+
2630
private _cacheDirectoryName: string = 'image-loader-cache';
2731

2832
set cacheDirectoryName(name: string) {

src/providers/image-loader.ts

Lines changed: 103 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Injectable } from '@angular/core';
22
import { File, FileEntry, FileReader, Transfer } from 'ionic-native';
33
import { ImageLoaderConfig } from "./image-loader-config";
4+
import * as _ from 'lodash';
45

56
declare var cordova: any;
67

@@ -39,6 +40,20 @@ export class ImageLoader {
3940

4041
private processing: number = 0;
4142

43+
private cacheIndex: Array<{
44+
name: string;
45+
modificationTime: Date;
46+
size: number;
47+
}> = [];
48+
49+
private currentCacheSize: number = 0;
50+
51+
private indexed: boolean = false;
52+
53+
private get shouldIndex() {
54+
return (this.config.maxCacheAge > -1) || (this.config.maxCacheSize > -1);
55+
}
56+
4257
constructor(private config: ImageLoaderConfig) {
4358
if (window.location.protocol === 'http:' || window.location.protocol === 'https:') {
4459
// we are running on a browser, or using livereload
@@ -113,9 +128,7 @@ export class ImageLoader {
113128

114129
let getImage = () => {
115130
this.getCachedImagePath(imageUrl)
116-
.then(imagePath => {
117-
resolve(imagePath);
118-
})
131+
.then(resolve)
119132
.catch(() => {
120133
// image doesn't exist in cache, lets fetch it and save it
121134
this.addItemToQueue(imageUrl, resolve, reject);
@@ -198,9 +211,13 @@ export class ImageLoader {
198211

199212
let localPath = cordova.file.cacheDirectory + this.config.cacheDirectoryName + '/' + this.createFileName(currentItem.imageUrl);
200213
this.downloadImage(currentItem.imageUrl, localPath)
201-
.then(() => {
202-
currentItem.resolve(localPath);
214+
.then((file: FileEntry) => {
215+
216+
if (this.shouldIndex) {
217+
this.addFileToIndex(file).then(this.maintainCacheSize.bind(this));
218+
}
203219

220+
currentItem.resolve(localPath);
204221
done();
205222
})
206223
.catch((e) => {
@@ -226,33 +243,100 @@ export class ImageLoader {
226243
}
227244

228245
this.cacheDirectoryExists
229-
.then(() => {
246+
.catch(() => {
247+
// doesn't exist
248+
return this.createCacheDirectory(replace)
249+
.catch(e => {
230250

231-
// exists
251+
this.throwError(e);
252+
this.isInit = true;
232253

254+
});
255+
})
256+
.then(() => this.indexCache())
257+
.then(() => {
233258
this.isCacheReady = true;
234259
this.isInit = true;
260+
});
235261

236-
})
237-
.catch(() => {
262+
}
238263

239-
// doesn't exist
264+
/**
265+
* Adds a file to index.
266+
* Also deletes any files if they are older than the set maximum cache age.
267+
* @param file {FileEntry} File to index
268+
* @returns {Promise<any>}
269+
*/
270+
private addFileToIndex(file: FileEntry): Promise<any> {
271+
return new Promise<any>((resolve, reject) => file.getMetadata(resolve, reject))
272+
.then(metadata => {
273+
274+
if (
275+
this.config.maxCacheAge > -1
276+
&& (Date.now() - metadata.modificationTime.getTime()) > this.config.maxCacheAge
277+
) {
278+
// file age exceeds maximum cache age
279+
return File.removeFile(cordova.file.cacheDirectory + this.config.cacheDirectoryName, file.name);
280+
} else {
281+
282+
// file age doesn't exceed maximum cache age, or maximum cache age isn't set
283+
this.currentCacheSize += metadata.size;
284+
285+
// add item to index
286+
this.cacheIndex.push({
287+
name: file.name,
288+
modificationTime: metadata.modificationTime,
289+
size: metadata.size
290+
});
240291

241-
this.createCacheDirectory(replace)
242-
.then(() => {
292+
return Promise.resolve();
243293

244-
this.isCacheReady = true;
245-
this.isInit = true;
294+
}
246295

247-
})
248-
.catch(e => {
296+
});
297+
}
249298

250-
this.throwError(e);
251-
this.isInit = true;
299+
/**
300+
* Indexes the cache if necessary
301+
* @returns {any}
302+
*/
303+
private indexCache(): Promise<void> {
252304

253-
});
305+
// only index if needed, to save resources
306+
if (!this.shouldIndex) return Promise.resolve();
307+
308+
this.cacheIndex = [];
254309

310+
return File.listDir(cordova.file.cacheDirectory, this.config.cacheDirectoryName)
311+
.then(files => Promise.all(files.map(this.addFileToIndex.bind(this))))
312+
.then(() => {
313+
this.cacheIndex = _.sortBy(this.cacheIndex, 'modificationTime');
314+
this.indexed = true;
315+
return Promise.resolve();
316+
})
317+
.catch(e => {
318+
this.throwError(e);
319+
return Promise.resolve();
255320
});
321+
}
322+
323+
/**
324+
* This method runs every time a new file is added.
325+
* It checks the cache size and ensures that it doesn't exceed the maximum cache size set in the config.
326+
* If the limit is reached, it will delete old images to create free space.
327+
*/
328+
private maintainCacheSize(): void {
329+
330+
if (this.config.maxCacheSize > -1 && this.indexed) {
331+
332+
// we exceeded max cache size
333+
while (this.currentCacheSize > this.config.maxCacheSize) {
334+
let file = this.cacheIndex.splice(0,1)[0];
335+
File.removeFile(cordova.file.cacheDirectory + this.config.cacheDirectoryName, file.name);
336+
this.currentCacheSize -= file.size;
337+
}
338+
339+
}
256340

257341
}
258342

0 commit comments

Comments
 (0)