소스 검색

Add IOS implementation for #43

Ben Hsieh 8 년 전
부모
커밋
17f2ab9935
3개의 변경된 파일200개의 추가작업 그리고 32개의 파일을 삭제
  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 파일 보기

@@ -236,16 +236,13 @@ RCT_EXPORT_METHOD(exists:(NSString *)path callback:(RCTResponseSenderBlock)callb
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 247
 RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding appendData:(BOOL)append callback:(RCTResponseSenderBlock)callback) {
251 248
     RNFetchBlobFS * fileStream = [[RNFetchBlobFS alloc] initWithBridgeRef:self.bridge];
@@ -412,16 +409,29 @@ RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callb
412 409
 }
413 410
 
414 411
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
412
+    
415 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 436
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
427 437
     

+ 3
- 0
src/ios/RNFetchBlobFS.h 파일 보기

@@ -51,6 +51,8 @@
51 51
 + (void) writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
52 52
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
53 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 57
 // constructor
56 58
 - (id) init;
@@ -61,6 +63,7 @@
61 63
 - (void) openWithDestination;
62 64
 - (void) openWithId;
63 65
 - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)encode appendData:(BOOL)append;
66
+- (void) startAssetReadStream:(NSData *)assetUrl;
64 67
 
65 68
 // file stream write data
66 69
 - (void)write:(NSData *) chunk;

+ 170
- 15
src/ios/RNFetchBlobFS.m 파일 보기

@@ -14,6 +14,7 @@
14 14
 #import "RCTEventDispatcher.h"
15 15
 #import "RNFetchBlobFS.h"
16 16
 #import "RNFetchBlobConst.h"
17
+@import AssetsLibrary;
17 18
 
18 19
 NSMutableDictionary *fileStreams = nil;
19 20
 
@@ -49,6 +50,18 @@ NSMutableDictionary *fileStreams = nil;
49 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 65
 + (NSString *) getCacheDir {
53 66
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
54 67
 }
@@ -84,6 +97,121 @@ NSMutableDictionary *fileStreams = nil;
84 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 215
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
88 216
     @try {
89 217
         NSFileManager * fm = [NSFileManager defaultManager];
@@ -175,6 +303,37 @@ NSMutableDictionary *fileStreams = nil;
175 303
 + (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
176 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 337
         NSFileManager * fm = [NSFileManager defaultManager];
179 338
         NSError *err = nil;
180 339
         BOOL exists = [fm fileExistsAtPath:path];
@@ -325,7 +484,16 @@ NSMutableDictionary *fileStreams = nil;
325 484
     self.encoding = encoding;
326 485
     self.path = path;
327 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 497
     // NSStream needs a runloop so let's create a run loop for it
330 498
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
331 499
     // start NSStream is a runloop
@@ -334,7 +502,7 @@ NSMutableDictionary *fileStreams = nil;
334 502
                             forMode:NSDefaultRunLoopMode];
335 503
         [inStream open];
336 504
         [[NSRunLoop currentRunLoop] run];
337
-
505
+        
338 506
     });
339 507
 }
340 508
 
@@ -349,19 +517,6 @@ NSMutableDictionary *fileStreams = nil;
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 520
 #pragma mark RNFetchBlobFS read stream delegate
366 521
 
367 522
 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {