Browse Source

Add IOS implementation for #43

Ben Hsieh 8 years ago
parent
commit
17f2ab9935
3 changed files with 200 additions and 32 deletions
  1. 27
    17
      src/ios/RNFetchBlob/RNFetchBlob.m
  2. 3
    0
      src/ios/RNFetchBlobFS.h
  3. 170
    15
      src/ios/RNFetchBlobFS.m

+ 27
- 17
src/ios/RNFetchBlob/RNFetchBlob.m View File

236
     
236
     
237
 }
237
 }
238
 
238
 
239
-RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
240
-    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
241
-    if(bufferSize == nil) {
242
-        if([[encoding lowercaseString] isEqualToString:@"base64"])
243
-            bufferSize = 4095;
244
-        else
245
-            bufferSize = 4096;
246
-    }
247
-    [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
248
-}
239
+RCT_EXPORT_METHOD(writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
240
+    [RNFetchBlobFS writeFile:path encoding:encoding data:data append:append resolver:resolve rejecter:reject];
241
+})
242
+
243
+RCT_EXPORT_METHOD(writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
244
+    [RNFetchBlobFS writeFileArray:path data:data append:append resolver:resolve rejecter:reject];
245
+})
249
 
246
 
250
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
247
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
251
     RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
248
     RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
412
 }
409
 }
413
 
410
 
414
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
411
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
412
+    
415
     [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject];
413
     [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject];
416
 })
414
 })
417
 
415
 
418
-RCT_EXPORT_METHOD(writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
419
-    [RNFetchBlobFS writeFile:path encoding:encoding data:data append:append resolver:resolve rejecter:reject];
420
-})
421
-
422
-RCT_EXPORT_METHOD(writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
423
-    [RNFetchBlobFS writeFileArray:path data:data append:append resolver:resolve rejecter:reject];
424
-})
416
+RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
417
+    
418
+    RNFetchBlobFS *fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
419
+    if(bufferSize == nil) {
420
+        if([[encoding lowercaseString] isEqualToString:@"base64"])
421
+            bufferSize = 4095;
422
+        else
423
+            bufferSize = 4096;
424
+    }
425
+    // read asset stream
426
+    if([path hasPrefix:@"assets-library://"])
427
+    {
428
+        
429
+    }
430
+    else
431
+    {
432
+        [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
433
+    }
434
+}
425
 
435
 
426
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
436
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
427
     
437
     

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

51
 + (void) writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
51
 + (void) writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
52
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
52
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
53
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
53
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
54
++ (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock;
55
++ (NSString *) getPathOfAsset:(NSString *)assetURI;
54
 
56
 
55
 // constructor
57
 // constructor
56
 - (id) init;
58
 - (id) init;
61
 - (void) openWithDestination;
63
 - (void) openWithDestination;
62
 - (void) openWithId;
64
 - (void) openWithId;
63
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
65
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
66
+- (void) startAssetReadStream:(NSData *)assetUrl;
64
 
67
 
65
 // file stream write data
68
 // file stream write data
66
 - (void)write:(NSData *) chunk;
69
 - (void)write:(NSData *) chunk;

+ 170
- 15
src/ios/RNFetchBlobFS.m View File

14
 #import "RCTEventDispatcher.h"
14
 #import "RCTEventDispatcher.h"
15
 #import "RNFetchBlobFS.h"
15
 #import "RNFetchBlobFS.h"
16
 #import "RNFetchBlobConst.h"
16
 #import "RNFetchBlobConst.h"
17
+@import AssetsLibrary;
17
 
18
 
18
 NSMutableDictionary *fileStreams = nil;
19
 NSMutableDictionary *fileStreams = nil;
19
 
20
 
49
     [fileStreams setValue:instance forKey:uuid];
50
     [fileStreams setValue:instance forKey:uuid];
50
 }
51
 }
51
 
52
 
53
++(NSString *) getPathOfAsset:(NSString *)assetURI
54
+{
55
+    // get file path of an app asset
56
+    if([assetURI hasPrefix:@"bundle-assets://"])
57
+    {
58
+        assetURI = [assetURI stringByReplacingOccurrencesOfString:@"bundle-assets://" withString:@""];
59
+        assetURI = [[NSBundle mainBundle] pathForResource: [assetURI stringByDeletingPathExtension]
60
+                                               ofType: [assetURI pathExtension]];
61
+    }
62
+    return assetURI;
63
+}
64
+
52
 + (NSString *) getCacheDir {
65
 + (NSString *) getCacheDir {
53
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
66
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
54
 }
67
 }
84
     return tempPath;
97
     return tempPath;
85
 }
98
 }
86
 
99
 
100
+- (void) startAssetReadStream:(NSData *)assetUrl
101
+{
102
+    ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
103
+    {
104
+        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
105
+        dispatch_async(queue, ^ {
106
+            NSString * streamEventCode = [NSString stringWithFormat:@"RNFetchBlobStream+%@", self.path];
107
+            ALAssetRepresentation *rep = [myasset defaultRepresentation];
108
+            Byte *buffer = (Byte*)malloc(self.bufferSize);
109
+            NSUInteger cursor = [rep getBytes:buffer fromOffset:0 length:self.bufferSize error:nil];
110
+            while(cursor > 0)
111
+            {
112
+                cursor += [rep getBytes:buffer fromOffset:cursor length:self.bufferSize error:nil];
113
+                NSData * chunkData = [NSData dataWithBytes:buffer length:self.bufferSize];
114
+                NSString * encodedChunk = @"";
115
+                // emit data
116
+                if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
117
+                    encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
118
+                }
119
+                // when encoding is ASCII, send byte array data
120
+                else if ( [[self.encoding lowercaseString] isEqualToString:@"ascii"] ) {
121
+                    // RCTBridge only emits string data, so we have to create JSON byte array string
122
+                    NSMutableArray * asciiArray = [NSMutableArray array];
123
+                    unsigned char *bytePtr;
124
+                    if (chunkData.length > 0)
125
+                    {
126
+                        bytePtr = (unsigned char *)[chunkData bytes];
127
+                        NSInteger byteLen = chunkData.length/sizeof(uint8_t);
128
+                        for (int i = 0; i < byteLen; i++)
129
+                        {
130
+                            [asciiArray addObject:[NSNumber numberWithChar:bytePtr[i]]];
131
+                        }
132
+                    }
133
+                    
134
+                    [self.bridge.eventDispatcher
135
+                     sendDeviceEventWithName:streamEventCode
136
+                     body: @{
137
+                             @"event": FS_EVENT_DATA,
138
+                             @"detail": asciiArray
139
+                             }
140
+                     ];
141
+                    return;
142
+                }
143
+                // convert byte array to base64 data chunks
144
+                else if ( [[self.encoding lowercaseString] isEqualToString:@"base64"] ) {
145
+                    encodedChunk = [chunkData base64EncodedStringWithOptions:0];
146
+                }
147
+                // unknown encoding, send error event
148
+                else {
149
+                    [self.bridge.eventDispatcher
150
+                     sendDeviceEventWithName:streamEventCode
151
+                     body:@{
152
+                            @"event": FS_EVENT_ERROR,
153
+                            @"detail": @"unrecognized encoding"
154
+                            }
155
+                     ];
156
+                    return;
157
+                }
158
+                
159
+                [self.bridge.eventDispatcher
160
+                 sendDeviceEventWithName:streamEventCode
161
+                 body:@{
162
+                        @"event": FS_EVENT_DATA,
163
+                        @"detail": encodedChunk
164
+                        }
165
+                 ];
166
+            }
167
+            free(buffer);
168
+        });
169
+        
170
+    };
171
+    
172
+    ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *error)
173
+    {
174
+        
175
+    };
176
+    
177
+    if(assetUrl && [assetUrl length])
178
+    {
179
+        NSURL *asseturl = [NSURL URLWithString:assetUrl];
180
+        ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
181
+        [assetslibrary assetForURL:asseturl
182
+                       resultBlock:resultblock
183
+                      failureBlock:failureblock];
184
+    }
185
+}
186
+
187
+// read system asset file
188
++ (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock
189
+{
190
+    
191
+    ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
192
+    {
193
+        ALAssetRepresentation *rep = [myasset defaultRepresentation];
194
+        Byte *buffer = (Byte*)malloc(rep.size);
195
+        NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
196
+        NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
197
+        completionBlock(data);
198
+    };
199
+    
200
+    ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *error)
201
+    {
202
+        failBlock(error);
203
+    };
204
+    
205
+    if(assetUrl && [assetUrl length])
206
+    {
207
+        NSURL *asseturl = [NSURL URLWithString:assetUrl];
208
+        ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
209
+        [assetslibrary assetForURL:asseturl
210
+                       resultBlock:resultblock
211
+                      failureBlock:failureblock];
212
+    }
213
+}
214
+
87
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
215
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
88
     @try {
216
     @try {
89
         NSFileManager * fm = [NSFileManager defaultManager];
217
         NSFileManager * fm = [NSFileManager defaultManager];
175
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
303
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
176
     @try
304
     @try
177
     {
305
     {
306
+        // before start reading file, we have to check if the `path` contains any special prefix
307
+        // if the `path` begins with the following prefix then it will need special handling.
308
+        //      "assets-library://" this kind of path usually comes from camera roll, should use it's own readFile implementation
309
+        //      "bundle-assets://" this means an asset inside app bundle, usually we only have to convert it into normal file path
310
+        if([path hasPrefix:@"assets-library://"])
311
+        {
312
+            [[self class] readAssetFile:path completionBlock:^(NSData * content)
313
+            {
314
+                if([[encoding lowercaseString] isEqualToString:@"utf8"]) {
315
+                    resolve([[NSString alloc] initWithData:content encoding:NSUTF8StringEncoding]);
316
+                }
317
+                else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
318
+                    resolve([content base64EncodedStringWithOptions:0]);
319
+                }
320
+                else if ([[encoding lowercaseString] isEqualToString:@"ascii"]) {
321
+                    NSMutableArray * resultArray = [NSMutableArray array];
322
+                    char * bytes = [content bytes];
323
+                    for(int i=0;i<[content length];i++) {
324
+                        [resultArray addObject:[NSNumber numberWithChar:bytes[i]]];
325
+                    }
326
+                    resolve(resultArray);
327
+                }
328
+            } failBlock:^(NSError *err) {
329
+                @throw @"RNFetchBlobFS readFile error", @"failed to read asset", path;
330
+            }];
331
+            return ;
332
+        }
333
+        
334
+        // normalize the file path
335
+        path = [[self class]getPathOfAsset:path];
336
+        
178
         NSFileManager * fm = [NSFileManager defaultManager];
337
         NSFileManager * fm = [NSFileManager defaultManager];
179
         NSError *err = nil;
338
         NSError *err = nil;
180
         BOOL exists = [fm fileExistsAtPath:path];
339
         BOOL exists = [fm fileExistsAtPath:path];
325
     self.encoding = encoding;
484
     self.encoding = encoding;
326
     self.path = path;
485
     self.path = path;
327
     self.bufferSize = bufferSize;
486
     self.bufferSize = bufferSize;
328
-
487
+    
488
+    if([path hasPrefix:@"assets-library://"])
489
+    {
490
+     
491
+        return;
492
+    }
493
+    
494
+    // normalize file path
495
+    path = [[self class] getPathOfAsset:path];
496
+    
329
     // NSStream needs a runloop so let's create a run loop for it
497
     // NSStream needs a runloop so let's create a run loop for it
330
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
498
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
331
     // start NSStream is a runloop
499
     // start NSStream is a runloop
334
                             forMode:NSDefaultRunLoopMode];
502
                             forMode:NSDefaultRunLoopMode];
335
         [inStream open];
503
         [inStream open];
336
         [[NSRunLoop currentRunLoop] run];
504
         [[NSRunLoop currentRunLoop] run];
337
-
505
+        
338
     });
506
     });
339
 }
507
 }
340
 
508
 
349
 
517
 
350
 }
518
 }
351
 
519
 
352
-void runOnMainQueueWithoutDeadlocking(void (^block)(void))
353
-{
354
-    if ([NSThread isMainThread])
355
-    {
356
-        block();
357
-    }
358
-    else
359
-    {
360
-        dispatch_sync(dispatch_get_main_queue(), block);
361
-    }
362
-}
363
-
364
-
365
 #pragma mark RNFetchBlobFS read stream delegate
520
 #pragma mark RNFetchBlobFS read stream delegate
366
 
521
 
367
 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
522
 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {