1
1
import { Injectable } from '@angular/core' ;
2
2
import { File , FileEntry , FileReader , Transfer } from 'ionic-native' ;
3
3
import { ImageLoaderConfig } from "./image-loader-config" ;
4
+ import * as _ from 'lodash' ;
4
5
5
6
declare var cordova : any ;
6
7
@@ -39,6 +40,20 @@ export class ImageLoader {
39
40
40
41
private processing : number = 0 ;
41
42
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
+
42
57
constructor ( private config : ImageLoaderConfig ) {
43
58
if ( window . location . protocol === 'http:' || window . location . protocol === 'https:' ) {
44
59
// we are running on a browser, or using livereload
@@ -113,9 +128,7 @@ export class ImageLoader {
113
128
114
129
let getImage = ( ) => {
115
130
this . getCachedImagePath ( imageUrl )
116
- . then ( imagePath => {
117
- resolve ( imagePath ) ;
118
- } )
131
+ . then ( resolve )
119
132
. catch ( ( ) => {
120
133
// image doesn't exist in cache, lets fetch it and save it
121
134
this . addItemToQueue ( imageUrl , resolve , reject ) ;
@@ -198,9 +211,13 @@ export class ImageLoader {
198
211
199
212
let localPath = cordova . file . cacheDirectory + this . config . cacheDirectoryName + '/' + this . createFileName ( currentItem . imageUrl ) ;
200
213
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
+ }
203
219
220
+ currentItem . resolve ( localPath ) ;
204
221
done ( ) ;
205
222
} )
206
223
. catch ( ( e ) => {
@@ -226,33 +243,100 @@ export class ImageLoader {
226
243
}
227
244
228
245
this . cacheDirectoryExists
229
- . then ( ( ) => {
246
+ . catch ( ( ) => {
247
+ // doesn't exist
248
+ return this . createCacheDirectory ( replace )
249
+ . catch ( e => {
230
250
231
- // exists
251
+ this . throwError ( e ) ;
252
+ this . isInit = true ;
232
253
254
+ } ) ;
255
+ } )
256
+ . then ( ( ) => this . indexCache ( ) )
257
+ . then ( ( ) => {
233
258
this . isCacheReady = true ;
234
259
this . isInit = true ;
260
+ } ) ;
235
261
236
- } )
237
- . catch ( ( ) => {
262
+ }
238
263
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
+ } ) ;
240
291
241
- this . createCacheDirectory ( replace )
242
- . then ( ( ) => {
292
+ return Promise . resolve ( ) ;
243
293
244
- this . isCacheReady = true ;
245
- this . isInit = true ;
294
+ }
246
295
247
- } )
248
- . catch ( e => {
296
+ } ) ;
297
+ }
249
298
250
- this . throwError ( e ) ;
251
- this . isInit = true ;
299
+ /**
300
+ * Indexes the cache if necessary
301
+ * @returns {any }
302
+ */
303
+ private indexCache ( ) : Promise < void > {
252
304
253
- } ) ;
305
+ // only index if needed, to save resources
306
+ if ( ! this . shouldIndex ) return Promise . resolve ( ) ;
307
+
308
+ this . cacheIndex = [ ] ;
254
309
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 ( ) ;
255
320
} ) ;
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
+ }
256
340
257
341
}
258
342
0 commit comments