@@ -25,6 +25,7 @@ import {
25
25
delayWhen ,
26
26
concatAll ,
27
27
take ,
28
+ tap ,
28
29
} from 'rxjs/operators'
29
30
import uuid from 'react-native-uuid'
30
31
import { CacheStrategy } from '.'
@@ -33,6 +34,7 @@ import { HeaderFn } from './types'
33
34
export interface CacheFileInfo {
34
35
path : string | null
35
36
fileName : string
37
+ md5 ?: string
36
38
}
37
39
38
40
/**
@@ -270,6 +272,7 @@ export class FileSystem {
270
272
FileSystem . cacheObservables [ fileName ] . next ( {
271
273
path : 'file://' + path ,
272
274
fileName,
275
+ md5 : await RNFS . hash ( path , 'md5' ) . catch ( ( ) => undefined ) ,
273
276
} )
274
277
}
275
278
@@ -301,26 +304,54 @@ export class FileSystem {
301
304
// Logic here prunes cache directory on "cache" writes to ensure cache doesn't get too large.
302
305
from ( cacheDirExists ? this . pruneCache ( ) : RNFS . mkdir ( this . baseFilePath ) ) ,
303
306
) ,
304
- mergeMap ( ( ) => from ( RNFS . stat ( path ) ) . pipe ( catchError ( ( ) => of ( null ) ) ) ) ,
305
- // Hit network and download file to local disk.
306
- mergeMap ( ( stat ) =>
307
- from (
308
- RNFS . downloadFile ( {
307
+ mergeMap ( ( ) => from ( RNFS . exists ( path ) ) ) ,
308
+ mergeMap ( ( alreadyExists ) =>
309
+ // Hit network and download file to local disk.
310
+ defer ( async ( ) => {
311
+ let beginResult : RNFS . DownloadBeginCallbackResult | undefined
312
+
313
+ const downloadResult = await RNFS . downloadFile ( {
309
314
fromUrl : url ,
310
315
toFile : path ,
311
316
headers,
312
- } ) . promise ,
313
- ) . pipe (
314
- // Only need to emit or throw errors if the file has changed or this is the first download
315
- filter ( ( downloadResult ) => stat === null || downloadResult . statusCode === 200 ) ,
316
- map ( ( downloadResult ) => {
317
- if ( stat === null && downloadResult . statusCode !== 200 ) {
318
- throw new Error ( 'Request failed ' + downloadResult . statusCode )
317
+ begin : ( res : RNFS . DownloadBeginCallbackResult ) => {
318
+ beginResult = res
319
+ } ,
320
+ } ) . promise
321
+ return Promise . resolve ( { downloadResult, beginResult } )
322
+ } ) . pipe (
323
+ // If "304 Not Modified" response touch local file
324
+ tap ( ( { downloadResult } ) => {
325
+ if ( alreadyExists && downloadResult . statusCode === 304 ) {
326
+ // Unfortunately react-native-fs only shares headers for status >=200 && status < 300
327
+ RNFS . touch ( path , new Date ( ) , new Date ( ) )
319
328
}
320
-
329
+ } ) ,
330
+ // Only need to emit if the local file has changed
331
+ filter ( ( { downloadResult } ) => downloadResult . statusCode === 200 ) ,
332
+ mergeMap ( ( { downloadResult, beginResult } ) =>
333
+ defer ( async ( ) => {
334
+ if ( beginResult !== undefined && 'ETag' in beginResult . headers ) {
335
+ return {
336
+ md5 : beginResult . headers [ 'ETag' ] ,
337
+ }
338
+ }
339
+ return {
340
+ md5 : await RNFS . hash ( path , 'md5' ) . catch ( ( ) => undefined ) ,
341
+ }
342
+ } ) . pipe (
343
+ map ( ( extra ) => ( {
344
+ downloadResult,
345
+ beginResult,
346
+ extra,
347
+ } ) ) ,
348
+ ) ,
349
+ ) ,
350
+ map ( ( { extra } ) => {
321
351
return {
322
352
path : 'file://' + path ,
323
353
fileName : pathLib . basename ( path ) ,
354
+ md5 : extra . md5 ,
324
355
}
325
356
} ) ,
326
357
) ,
@@ -454,27 +485,29 @@ export class FileSystem {
454
485
455
486
const subject$ = new ReplaySubject < CacheFileInfo > ( )
456
487
457
- const obs$ = from ( RNFS . stat ( this . baseFilePath + fileName ) ) . pipe (
488
+ const obs$ = from ( RNFS . hash ( this . baseFilePath + fileName , 'md5' ) ) . pipe (
458
489
catchError ( ( ) => of ( null ) ) ,
459
- switchMap ( ( stat ) => {
460
- if ( stat !== null ) {
490
+ switchMap ( ( md5 ) => {
491
+ if ( md5 !== null ) {
461
492
switch ( cacheStrategy ) {
462
493
case 'immutable' : {
463
494
return of ( {
464
495
path : 'file://' + this . baseFilePath + fileName ,
465
496
fileName,
497
+ md5,
466
498
} as CacheFileInfo )
467
499
}
468
500
case 'mutable' : {
469
501
return from ( [
470
502
of ( {
471
503
path : 'file://' + this . baseFilePath + fileName ,
472
504
fileName,
505
+ md5,
473
506
} as CacheFileInfo ) ,
474
507
defer ( async ( ) => ( headers instanceof Function ? await headers ( ) : headers ) ) . pipe (
475
508
mergeMap ( ( headers ) =>
476
509
this . fetchFile ( url , fileName , {
477
- 'if-modified-since ' : new Date ( stat . mtime ) . toUTCString ( ) ,
510
+ 'if-none-match ' : md5 ,
478
511
...headers ,
479
512
} ) ,
480
513
) ,
0 commit comments