|
@@ -9,6 +9,7 @@
|
9
|
9
|
|
10
|
10
|
#import <Foundation/Foundation.h>
|
11
|
11
|
#import "RCTBridge.h"
|
|
12
|
+#import "RNFetchBlob.h"
|
12
|
13
|
#import "RCTEventDispatcher.h"
|
13
|
14
|
#import "RNFetchBlobFS.h"
|
14
|
15
|
#import "RNFetchBlobConst.h"
|
|
@@ -98,93 +99,99 @@ NSMutableDictionary *fileStreams = nil;
|
98
|
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
|
197
|
# pragma write file from file
|
|
@@ -544,35 +551,6 @@ NSMutableDictionary *fileStreams = nil;
|
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
|
554
|
// Slice a file into another file, generally for support Blob implementation.
|
577
|
555
|
+ (void)slice:(NSString *)path
|
578
|
556
|
dest:(NSString *)dest
|
|
@@ -690,118 +668,6 @@ NSMutableDictionary *fileStreams = nil;
|
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
|
672
|
# pragma mark - get absolute path of resource
|
807
|
673
|
|