Browse Source

fs.readStream redesign

Ben Hsieh 8 years ago
parent
commit
765de2308f

+ 10
- 9
src/class/RNFetchBlobReadStream.js View File

8
   DeviceEventEmitter,
8
   DeviceEventEmitter,
9
   NativeAppEventEmitter,
9
   NativeAppEventEmitter,
10
 } from 'react-native'
10
 } from 'react-native'
11
+import UUID from '../utils/uuid'
11
 
12
 
12
 const RNFetchBlob = NativeModules.RNFetchBlob
13
 const RNFetchBlob = NativeModules.RNFetchBlob
13
 const emitter = DeviceEventEmitter
14
 const emitter = DeviceEventEmitter
29
     this._onData = () => {}
30
     this._onData = () => {}
30
     this._onEnd = () => {}
31
     this._onEnd = () => {}
31
     this._onError = () => {}
32
     this._onError = () => {}
33
+    this.streamId = 'RNFBRS'+ UUID()
32
 
34
 
33
     // register for file stream event
35
     // register for file stream event
34
-    let subscription = emitter.addListener(`RNFetchBlobStream+${this.path}`, (e) => {
35
-
36
+    let subscription = emitter.addListener(this.streamId, (e) => {
36
       let {event, detail} = e
37
       let {event, detail} = e
37
       if(this._onData && event === 'data')
38
       if(this._onData && event === 'data')
38
         this._onData(detail)
39
         this._onData(detail)
56
 
57
 
57
   open() {
58
   open() {
58
     if(!this.closed)
59
     if(!this.closed)
59
-      RNFetchBlob.readStream(this.path, this.encoding, this.bufferSize || 0)
60
+      RNFetchBlob.readStream(this.path, this.encoding, this.bufferSize || 0, this.streamId)
60
     else
61
     else
61
       throw new Error('Stream closed')
62
       throw new Error('Stream closed')
62
   }
63
   }
63
 
64
 
64
   onData(fn) {
65
   onData(fn) {
65
-    if(this.encoding.toLowerCase() === 'ascii')
66
-      this._onData = (data) => {
67
-        fn(data)
68
-      }
69
-    else
70
-      this._onData = fn
66
+    // if(this.encoding.toLowerCase() === 'ascii')
67
+    //   this._onData = (data) => {
68
+    //     fn(data)
69
+    //   }
70
+    // else
71
+    this._onData = fn
71
   }
72
   }
72
 
73
 
73
   onError(fn) {
74
   onError(fn) {

+ 1
- 0
src/ios/RNFetchBlob/RNFetchBlob.h View File

17
 
17
 
18
 @property (nonatomic) NSString * filePathPrefix;
18
 @property (nonatomic) NSString * filePathPrefix;
19
 
19
 
20
++ (RCTBridge *)getRCTBridge;
20
 
21
 
21
 @end
22
 @end
22
 
23
 

+ 15
- 5
src/ios/RNFetchBlob/RNFetchBlob.m View File

14
 #import "RNFetchBlobReqBuilder.h"
14
 #import "RNFetchBlobReqBuilder.h"
15
 
15
 
16
 
16
 
17
+RCTBridge * bridgeRef;
18
+
17
 ////////////////////////////////////////
19
 ////////////////////////////////////////
18
 //
20
 //
19
 //  Exported native methods
21
 //  Exported native methods
31
     return dispatch_queue_create("RNFetchBlob.queue", DISPATCH_QUEUE_SERIAL);
33
     return dispatch_queue_create("RNFetchBlob.queue", DISPATCH_QUEUE_SERIAL);
32
 }
34
 }
33
 
35
 
36
++ (RCTBridge *)getRCTBridge
37
+{
38
+    return bridgeRef;
39
+}
40
+
34
 RCT_EXPORT_MODULE();
41
 RCT_EXPORT_MODULE();
35
 
42
 
36
 - (id) init {
43
 - (id) init {
41
     if(![[NSFileManager defaultManager] fileExistsAtPath: [RNFetchBlobFS getTempPath] isDirectory:&isDir]) {
48
     if(![[NSFileManager defaultManager] fileExistsAtPath: [RNFetchBlobFS getTempPath] isDirectory:&isDir]) {
42
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
49
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
43
     }
50
     }
51
+    bridgeRef = _bridge;
44
     return self;
52
     return self;
45
 }
53
 }
46
 
54
 
366
 })
374
 })
367
 
375
 
368
 #pragma mark - fs.readStream
376
 #pragma mark - fs.readStream
369
-RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
377
+RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize streamId:(NSString *)streamId
378
+{
370
 
379
 
371
-    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
380
+//    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
372
     if(bufferSize == nil) {
381
     if(bufferSize == nil) {
373
         if([[encoding lowercaseString] isEqualToString:@"base64"])
382
         if([[encoding lowercaseString] isEqualToString:@"base64"])
374
             bufferSize = 4095;
383
             bufferSize = 4095;
375
         else
384
         else
376
             bufferSize = 4096;
385
             bufferSize = 4096;
377
     }
386
     }
378
-    // read asset stream
379
-    [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
380
-}
387
+    
388
+//    [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
389
+    [RNFetchBlobFS readStream:path encoding:encoding bufferSize:bufferSize streamId:streamId bridgeRef:_bridge];
390
+})
381
 
391
 
382
 #pragma mark - fs.getEnvionmentDirs
392
 #pragma mark - fs.getEnvionmentDirs
383
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
393
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {

+ 2
- 3
src/ios/RNFetchBlobFS.h View File

55
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
55
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
56
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject onComplete:(void (^)(NSData * content))onComplete;
56
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject onComplete:(void (^)(NSData * content))onComplete;
57
 + (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock;
57
 + (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock;
58
-+ (void)slice:(NSString *)path
58
++ (void) slice:(NSString *)path
59
          dest:(NSString *)dest
59
          dest:(NSString *)dest
60
         start:(nonnull NSNumber *)start
60
         start:(nonnull NSNumber *)start
61
           end:(nonnull NSNumber *)end
61
           end:(nonnull NSNumber *)end
64
      rejecter:(RCTPromiseRejectBlock)reject;
64
      rejecter:(RCTPromiseRejectBlock)reject;
65
 //+ (void) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append;
65
 //+ (void) writeFileFromFile:(NSString *)src toFile:(NSString *)dest append:(BOOL)append;
66
 + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest;
66
 + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest;
67
++ (void) readStream:(NSString *)uri encoding:(NSString * )encoding bufferSize:(int)bufferSize streamId:(NSString *)streamId bridgeRef:(RCTBridge *)bridgeRef;
67
 
68
 
68
 // constructor
69
 // constructor
69
 - (id) init;
70
 - (id) init;
73
 // file stream
74
 // file stream
74
 - (void) openWithDestination;
75
 - (void) openWithDestination;
75
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
76
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
76
-- (void) startAssetReadStream:(NSData *)assetUrl;
77
 
77
 
78
 // file stream write data
78
 // file stream write data
79
 - (void)write:(NSData *) chunk;
79
 - (void)write:(NSData *) chunk;
80
 - (void)writeEncodeChunk:(NSString *) chunk;
80
 - (void)writeEncodeChunk:(NSString *) chunk;
81
-- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize;
82
 
81
 
83
 - (void) closeInStream;
82
 - (void) closeInStream;
84
 - (void) closeOutStream;
83
 - (void) closeOutStream;

+ 83
- 217
src/ios/RNFetchBlobFS.m View File

9
 
9
 
10
 #import <Foundation/Foundation.h>
10
 #import <Foundation/Foundation.h>
11
 #import "RCTBridge.h"
11
 #import "RCTBridge.h"
12
+#import "RNFetchBlob.h"
12
 #import "RCTEventDispatcher.h"
13
 #import "RCTEventDispatcher.h"
13
 #import "RNFetchBlobFS.h"
14
 #import "RNFetchBlobFS.h"
14
 #import "RNFetchBlobConst.h"
15
 #import "RNFetchBlobConst.h"
98
     return tempPath;
99
     return tempPath;
99
 }
100
 }
100
 
101
 
101
-#pragma mark - read asset stream
102
+#pragma margk - readStream 
102
 
103
 
103
-- (void) startAssetReadStream:(NSString *)assetUrl
104
++ (void) readStream:(NSString *)uri
105
+           encoding:(NSString * )encoding
106
+         bufferSize:(int)bufferSize
107
+           streamId:(NSString *)streamId
108
+          bridgeRef:(RCTBridge *)bridgeRef
104
 {
109
 {
105
-    ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
106
-    {
107
-        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
108
-        dispatch_async(queue, ^ {
109
-            NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
110
-            ALAssetRepresentation *rep = [myasset defaultRepresentation];
111
-            Byte *buffer = (Byte*)malloc(self.bufferSize);
112
-            NSUInteger cursor = [rep getBytes:buffer fromOffset:0 length:self.bufferSize error:nil];
113
-            while(cursor > 0)
110
+    [[self class] getPathFromUri:uri completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
111
+    
112
+        RCTEventDispatcher * event = bridgeRef.eventDispatcher;
113
+        @try
114
+        {
115
+            int read = 0;
116
+            int chunkSize = bufferSize;
117
+            uint8_t * buffer[bufferSize];
118
+            
119
+            if(path != nil)
114
             {
120
             {
115
-                cursor += [rep getBytes:buffer fromOffset:cursor length:self.bufferSize error:nil];
116
-                NSData * chunkData = [NSData dataWithBytes:buffer length:self.bufferSize];
117
-                NSString * encodedChunk = @"";
118
-                // emit data
119
-                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
120
-                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
121
-                }
122
-                // when encoding is ASCII, send byte array data
123
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
124
-                    // RCTBridge only emits string data, so we have to create JSON byte array string
125
-                    NSMutableArray * asciiArray = [NSMutableArray array];
126
-                    unsigned char *bytePtr;
127
-                    if (chunkData.length > 0)
128
-                    {
129
-                        bytePtr = (unsigned char *)[chunkData bytes];
130
-                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
131
-                        for (int i = 0; i < byteLen; i++)
132
-                        {
133
-                            [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
134
-                        }
135
-                    }
136
-                    
137
-                    [self.bridge.eventDispatcher
138
-                     sendDeviceEventWithName:streamEventCode
139
-                     body: @{
140
-                             @"event": FS_EVENT_DATA,
141
-                             @"detail": asciiArray
142
-                             }
143
-                     ];
144
-                    return;
145
-                }
146
-                // convert byte array to base64 data chunks
147
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
148
-                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
121
+                NSInputStream * stream = [[NSInputStream alloc] initWithFileAtPath:path];
122
+                [stream open];
123
+                while((read = [stream read:buffer maxLength:bufferSize]) > 0)
124
+                {
125
+                    [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId event:event];
149
                 }
126
                 }
150
-                // unknown encoding, send error event
151
-                else {
152
-                    [self.bridge.eventDispatcher
153
-                     sendDeviceEventWithName:streamEventCode
154
-                     body:@{
155
-                            @"event": FS_EVENT_ERROR,
156
-                            @"detail": @"unrecognized encoding"
157
-                            }
158
-                     ];
159
-                    return;
127
+                [stream close];
128
+            }
129
+            else if (asset != nil)
130
+            {
131
+                int cursor = 0;
132
+                NSError * err;
133
+                while((read = [asset getBytes:buffer fromOffset:cursor length:bufferSize error:&err]) > 0)
134
+                {
135
+                    cursor += read;
136
+                    [[self class] emitDataChunks:[NSData dataWithBytes:buffer length:read] encoding:encoding streamId:streamId event:event];
160
                 }
137
                 }
161
-                
162
-                [self.bridge.eventDispatcher
163
-                 sendDeviceEventWithName:streamEventCode
164
-                 body:@{
165
-                        @"event": FS_EVENT_DATA,
166
-                        @"detail": encodedChunk
167
-                        }
168
-                 ];
169
             }
138
             }
170
-            free(buffer);
171
-        });
139
+            else
140
+            {
141
+                NSDictionary * payload = @{ @"event": FS_EVENT_ERROR, @"detail": @"RNFetchBlob.readStream unable to resolve URI" };
142
+                [event sendDeviceEventWithName:streamId body:payload];
143
+            }
144
+        }
145
+        @catch (NSError * err)
146
+        {
147
+            
148
+            NSDictionary * payload = @{ @"event": FS_EVENT_ERROR, @"detail": [NSString stringWithFormat:@"RNFetchBlob.readStream error %@", [err description]] };
149
+            [event sendDeviceEventWithName:streamId body:payload];
150
+        }
151
+        @finally
152
+        {
153
+            NSDictionary * payload = @{ @"event": FS_EVENT_END, @"detail": @"" };
154
+            [event sendDeviceEventWithName:streamId body:payload];
155
+        }
172
         
156
         
173
-    };
157
+    }];
174
     
158
     
175
-    ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *error)
159
+}
160
+
161
+// send read stream chunks via native event emitter
162
++ (void) emitDataChunks:(NSData *)data encoding:(NSString *) encoding streamId:(NSString *)streamId event:(RCTEventDispatcher *)event
163
+{
164
+    NSString * encodedChunk = @"";
165
+    if([[encoding lowercaseString] isEqualToString:@"utf8"])
176
     {
166
     {
177
-        
178
-    };
179
-    
180
-    if(assetUrl && [assetUrl length])
167
+        NSDictionary * payload = @{ @"event": FS_EVENT_DATA,  @"detail" : [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] };
168
+        [event sendDeviceEventWithName:streamId body:payload];
169
+    }
170
+    else if ([[encoding lowercaseString] isEqualToString:@"base64"])
181
     {
171
     {
182
-        NSURL *asseturl = [NSURL URLWithString:assetUrl];
183
-        ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
184
-        [assetslibrary assetForURL:asseturl
185
-                       resultBlock:resultblock
186
-                      failureBlock:failureblock];
172
+        NSDictionary * payload = @{ @"event": FS_EVENT_DATA,  @"detail" : [data base64EncodedStringWithOptions:0] };
173
+        [event sendDeviceEventWithName:streamId body:payload];
187
     }
174
     }
175
+    else if([[encoding lowercaseString] isEqualToString:@"ascii"])
176
+    {
177
+        // RCTBridge only emits string data, so we have to create JSON byte array string
178
+        NSMutableArray * asciiArray = [NSMutableArray array];
179
+        unsigned char *bytePtr;
180
+        if (data.length > 0)
181
+        {
182
+            bytePtr = (unsigned char *)[data bytes];
183
+            NSInteger byteLen = data.length/sizeof(uint8_t);
184
+            for (int i = 0; i < byteLen; i++)
185
+            {
186
+                [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
187
+            }
188
+        }
189
+
190
+        NSDictionary * payload = @{ @"event": FS_EVENT_DATA,  @"detail" : asciiArray };
191
+        [event sendDeviceEventWithName:streamId body:payload];
192
+    }
193
+    
194
+    
188
 }
195
 }
189
 
196
 
190
 # pragma write file from file
197
 # pragma write file from file
544
     
551
     
545
 }
552
 }
546
 
553
 
547
-- (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize {
548
-    
549
-    self.inStream = [[NSInputStream alloc] initWithFileAtPath:path];
550
-    self.inStream.delegate = self;
551
-    self.encoding = encoding;
552
-    self.path = path;
553
-    self.bufferSize = bufferSize;
554
-    
555
-    if([path hasPrefix:AL_PREFIX])
556
-    {
557
-        [self startAssetReadStream:path];
558
-        return;
559
-    }
560
-    
561
-    // normalize file path
562
-    path = [[self class] getPathOfAsset:path];
563
-    
564
-    // NSStream needs a runloop so let's create a run loop for it
565
-    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
566
-    // start NSStream is a runloop
567
-    dispatch_async(queue, ^ {
568
-        [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
569
-                            forMode:NSDefaultRunLoopMode];
570
-        [inStream open];
571
-        [[NSRunLoop currentRunLoop] run];
572
-        
573
-    });
574
-}
575
-
576
 // Slice a file into another file, generally for support Blob implementation.
554
 // Slice a file into another file, generally for support Blob implementation.
577
 + (void)slice:(NSString *)path
555
 + (void)slice:(NSString *)path
578
          dest:(NSString *)dest
556
          dest:(NSString *)dest
690
     
668
     
691
 }
669
 }
692
 
670
 
693
-#pragma mark RNFetchBlobFS read stream delegate
694
-
695
-- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
696
-    
697
-    NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
698
-    
699
-    switch(eventCode) {
700
-            
701
-            // write stream event
702
-        case NSStreamEventHasSpaceAvailable:
703
-        {
704
-            
705
-            
706
-        }
707
-            
708
-            // read stream incoming chunk
709
-        case NSStreamEventHasBytesAvailable:
710
-        {
711
-            NSMutableData * chunkData = [[NSMutableData alloc] init];
712
-            NSInteger chunkSize = 4096;
713
-            if([[self.encoding lowercaseString] isEqualToString:@"base64"])
714
-                chunkSize = 4095;
715
-            if(self.bufferSize > 0)
716
-                chunkSize = self.bufferSize;
717
-            uint8_t buf[chunkSize];
718
-            unsigned int len = 0;
719
-            len = [(NSInputStream *)stream read:buf maxLength:chunkSize];
720
-            // still have data in stream
721
-            if(len) {
722
-                [chunkData appendBytes:buf length:len];
723
-                // dispatch data event
724
-                NSString * encodedChunk = [NSString alloc];
725
-                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
726
-                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
727
-                }
728
-                // when encoding is ASCII, send byte array data
729
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
730
-                    // RCTBridge only emits string data, so we have to create JSON byte array string
731
-                    NSMutableArray * asciiArray = [NSMutableArray array];
732
-                    unsigned char *bytePtr;
733
-                    if (chunkData.length > 0)
734
-                    {
735
-                        bytePtr = (unsigned char *)[chunkData bytes];
736
-                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
737
-                        for (int i = 0; i < byteLen; i++)
738
-                        {
739
-                            [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
740
-                        }
741
-                    }
742
-                    
743
-                    [self.bridge.eventDispatcher
744
-                     sendDeviceEventWithName:streamEventCode
745
-                     body: @{
746
-                             @"event": FS_EVENT_DATA,
747
-                             @"detail": asciiArray
748
-                             }
749
-                     ];
750
-                    return;
751
-                }
752
-                // convert byte array to base64 data chunks
753
-                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
754
-                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
755
-                }
756
-                // unknown encoding, send error event
757
-                else {
758
-                    [self.bridge.eventDispatcher
759
-                     sendDeviceEventWithName:streamEventCode
760
-                     body:@{
761
-                            @"event": FS_EVENT_ERROR,
762
-                            @"detail": @"unrecognized encoding"
763
-                            }
764
-                     ];
765
-                    return;
766
-                }
767
-                
768
-                [self.bridge.eventDispatcher
769
-                 sendDeviceEventWithName:streamEventCode
770
-                 body:@{
771
-                        @"event": FS_EVENT_DATA,
772
-                        @"detail": encodedChunk
773
-                        }
774
-                 ];
775
-            }
776
-            // end of stream
777
-            else {
778
-                [self.bridge.eventDispatcher
779
-                 sendDeviceEventWithName:streamEventCode
780
-                 body:@{
781
-                        @"event": FS_EVENT_END,
782
-                        @"detail": @""
783
-                        }
784
-                 ];
785
-            }
786
-            break;
787
-        }
788
-            
789
-            // stream error
790
-        case NSStreamEventErrorOccurred:
791
-        {
792
-            [self.bridge.eventDispatcher
793
-             sendDeviceEventWithName:streamEventCode
794
-             body:@{
795
-                    @"event": FS_EVENT_ERROR,
796
-                    @"detail": @"RNFetchBlob error when read file with stream, file may not exists"
797
-                    }
798
-             ];
799
-            break;
800
-        }
801
-            
802
-    }
803
-    
804
-}
805
 
671
 
806
 # pragma mark - get absolute path of resource
672
 # pragma mark - get absolute path of resource
807
 
673