|  | @@ -35,6 +35,7 @@ NSString *const FS_EVENT_ERROR = @"error";
 | 
	
		
			
			| 35 | 35 |  
 | 
	
		
			
			| 36 | 36 |  @implementation FetchBlobFS
 | 
	
		
			
			| 37 | 37 |  
 | 
	
		
			
			|  | 38 | +
 | 
	
		
			
			| 38 | 39 |  @synthesize outStream;
 | 
	
		
			
			| 39 | 40 |  @synthesize inStream;
 | 
	
		
			
			| 40 | 41 |  @synthesize encoding;
 | 
	
	
		
			
			|  | @@ -43,8 +44,14 @@ NSString *const FS_EVENT_ERROR = @"error";
 | 
	
		
			
			| 43 | 44 |  @synthesize path;
 | 
	
		
			
			| 44 | 45 |  @synthesize bufferSize;
 | 
	
		
			
			| 45 | 46 |  
 | 
	
		
			
			| 46 |  | -
 | 
	
		
			
			| 47 |  | -
 | 
	
		
			
			|  | 47 | +// static member getter
 | 
	
		
			
			|  | 48 | ++ (NSArray *) getFileStreams {
 | 
	
		
			
			|  | 49 | +    
 | 
	
		
			
			|  | 50 | +    static NSMutableData *fileStreams = nil;
 | 
	
		
			
			|  | 51 | +    if(fileStreams == nil)
 | 
	
		
			
			|  | 52 | +        fileStreams = [[NSArray alloc] init];
 | 
	
		
			
			|  | 53 | +    return fileStreams;
 | 
	
		
			
			|  | 54 | +}
 | 
	
		
			
			| 48 | 55 |  
 | 
	
		
			
			| 49 | 56 |  + (NSString *) getCacheDir {
 | 
	
		
			
			| 50 | 57 |      return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
 | 
	
	
		
			
			|  | @@ -67,22 +74,40 @@ NSString *const FS_EVENT_ERROR = @"error";
 | 
	
		
			
			| 67 | 74 |  }
 | 
	
		
			
			| 68 | 75 |  
 | 
	
		
			
			| 69 | 76 |  
 | 
	
		
			
			|  | 77 | ++ (NSString *) getTempPath {
 | 
	
		
			
			|  | 78 | +    
 | 
	
		
			
			|  | 79 | +    return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingString:@"/RNFetchBlob_tmp"];
 | 
	
		
			
			|  | 80 | +}
 | 
	
		
			
			|  | 81 | +
 | 
	
		
			
			| 70 | 82 |  + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext {
 | 
	
		
			
			| 71 | 83 |      
 | 
	
		
			
			| 72 | 84 |      NSString * documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
 | 
	
		
			
			| 73 |  | -    NSString * filename = [NSString stringWithFormat:@"/RNFetchBlobTmp_%@", taskId];
 | 
	
		
			
			|  | 85 | +    NSString * filename = [NSString stringWithFormat:@"/RNFetchBlob_tmp/RNFetchBlobTmp_%@", taskId];
 | 
	
		
			
			| 74 | 86 |      if(ext != nil)
 | 
	
		
			
			| 75 | 87 |          filename = [filename stringByAppendingString: [NSString stringWithFormat:@".%@", ext]];
 | 
	
		
			
			| 76 | 88 |      NSString * tempPath = [documentDir stringByAppendingString: filename];
 | 
	
		
			
			| 77 | 89 |      return tempPath;
 | 
	
		
			
			| 78 | 90 |  }
 | 
	
		
			
			| 79 | 91 |  
 | 
	
		
			
			|  | 92 | ++ (BOOL) mkdir:(NSString *) path {
 | 
	
		
			
			|  | 93 | +    BOOL isDir;
 | 
	
		
			
			|  | 94 | +    NSError * err = nil;
 | 
	
		
			
			|  | 95 | +    // if temp folder not exists, create one
 | 
	
		
			
			|  | 96 | +    if(![[NSFileManager defaultManager] fileExistsAtPath: path isDirectory:&isDir]) {
 | 
	
		
			
			|  | 97 | +        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&err];
 | 
	
		
			
			|  | 98 | +    }
 | 
	
		
			
			|  | 99 | +    return err == nil;
 | 
	
		
			
			|  | 100 | +}
 | 
	
		
			
			|  | 101 | +
 | 
	
		
			
			|  | 102 | ++ (BOOL) exists:(NSString *) path {
 | 
	
		
			
			|  | 103 | +    return [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:NULL];
 | 
	
		
			
			|  | 104 | +}
 | 
	
		
			
			|  | 105 | +
 | 
	
		
			
			| 80 | 106 |  - (id)init {
 | 
	
		
			
			| 81 | 107 |      self = [super init];
 | 
	
		
			
			| 82 | 108 |      return self;
 | 
	
		
			
			| 83 | 109 |  }
 | 
	
		
			
			| 84 | 110 |  
 | 
	
		
			
			| 85 |  | -
 | 
	
		
			
			| 86 | 111 |  - (id)initWithCallback:(RCTResponseSenderBlock)callback {
 | 
	
		
			
			| 87 | 112 |      self = [super init];
 | 
	
		
			
			| 88 | 113 |      self.callback = callback;
 | 
	
	
		
			
			|  | @@ -95,15 +120,18 @@ NSString *const FS_EVENT_ERROR = @"error";
 | 
	
		
			
			| 95 | 120 |      return self;
 | 
	
		
			
			| 96 | 121 |  }
 | 
	
		
			
			| 97 | 122 |  
 | 
	
		
			
			| 98 |  | -- (void)openWithPath:(NSString *)destPath {
 | 
	
		
			
			|  | 123 | +- (NSString *)openWithPath:(NSString *)destPath {
 | 
	
		
			
			| 99 | 124 |      self.outStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:YES];
 | 
	
		
			
			| 100 | 125 |      [self.outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
 | 
	
		
			
			| 101 | 126 |      [self.outStream open];
 | 
	
		
			
			|  | 127 | +    NSString *uuid = [[NSUUID UUID] UUIDString];
 | 
	
		
			
			|  | 128 | +    self.streamId = uuid;
 | 
	
		
			
			|  | 129 | +    [[FetchBlobFS getFileStreams] setValue:self forKey:uuid];
 | 
	
		
			
			|  | 130 | +    return uuid;
 | 
	
		
			
			| 102 | 131 |  }
 | 
	
		
			
			| 103 | 132 |  
 | 
	
		
			
			| 104 |  | -
 | 
	
		
			
			| 105 | 133 |  // Write file chunk into an opened stream
 | 
	
		
			
			| 106 |  | -- (void)write:(NSData *) chunk toPath:(NSString *) path{
 | 
	
		
			
			|  | 134 | +- (void)write:(NSData *) chunk {
 | 
	
		
			
			| 107 | 135 |      NSUInteger left = [chunk length];
 | 
	
		
			
			| 108 | 136 |      NSUInteger nwr = 0;
 | 
	
		
			
			| 109 | 137 |      do {
 | 
	
	
		
			
			|  | @@ -134,7 +162,6 @@ NSString *const FS_EVENT_ERROR = @"error";
 | 
	
		
			
			| 134 | 162 |          [[NSRunLoop currentRunLoop] run];
 | 
	
		
			
			| 135 | 163 |      
 | 
	
		
			
			| 136 | 164 |      });
 | 
	
		
			
			| 137 |  | -    
 | 
	
		
			
			| 138 | 165 |  }
 | 
	
		
			
			| 139 | 166 |  
 | 
	
		
			
			| 140 | 167 |  // close file write stream
 | 
	
	
		
			
			|  | @@ -151,6 +178,8 @@ NSString *const FS_EVENT_ERROR = @"error";
 | 
	
		
			
			| 151 | 178 |      if(self.inStream != nil) {
 | 
	
		
			
			| 152 | 179 |          [self.inStream close];
 | 
	
		
			
			| 153 | 180 |          [self.inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
 | 
	
		
			
			|  | 181 | +        [[FetchBlobFS getFileStreams] setValue:nil forKey:self.streamId];
 | 
	
		
			
			|  | 182 | +        self.streamId = nil;
 | 
	
		
			
			| 154 | 183 |      }
 | 
	
		
			
			| 155 | 184 |      
 | 
	
		
			
			| 156 | 185 |  }
 | 
	
	
		
			
			|  | @@ -345,12 +374,12 @@ void runOnMainQueueWithoutDeadlocking(void (^block)(void))
 | 
	
		
			
			| 345 | 374 |      Boolean fileCache = [self.options valueForKey:CONFIG_USE_TEMP];
 | 
	
		
			
			| 346 | 375 |      NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
 | 
	
		
			
			| 347 | 376 |      if(path != nil) {
 | 
	
		
			
			| 348 |  | -        [self.fileStream write:data toPath:path];
 | 
	
		
			
			|  | 377 | +        [self.fileStream write:data];
 | 
	
		
			
			| 349 | 378 |      }
 | 
	
		
			
			| 350 | 379 |      // write to tmp file
 | 
	
		
			
			| 351 | 380 |      else if( fileCache != nil) {
 | 
	
		
			
			| 352 | 381 |          NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
 | 
	
		
			
			| 353 |  | -        [self.fileStream write:data toPath:[FetchBlobFS getTempPath:self.taskId withExtension:ext]];
 | 
	
		
			
			|  | 382 | +        [self.fileStream write:data];
 | 
	
		
			
			| 354 | 383 |      }
 | 
	
		
			
			| 355 | 384 |      // cache data in memory
 | 
	
		
			
			| 356 | 385 |      else {
 | 
	
	
		
			
			|  | @@ -457,6 +486,11 @@ RCT_EXPORT_MODULE();
 | 
	
		
			
			| 457 | 486 |  - (id) init {
 | 
	
		
			
			| 458 | 487 |      self = [super init];
 | 
	
		
			
			| 459 | 488 |      self.filePathPrefix = FILE_PREFIX;
 | 
	
		
			
			|  | 489 | +    BOOL isDir;
 | 
	
		
			
			|  | 490 | +    // if temp folder not exists, create one
 | 
	
		
			
			|  | 491 | +    if(![[NSFileManager defaultManager] fileExistsAtPath: [FetchBlobFS getTempPath] isDirectory:&isDir]) {
 | 
	
		
			
			|  | 492 | +        [[NSFileManager defaultManager] createDirectoryAtPath:[FetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
 | 
	
		
			
			|  | 493 | +    }
 | 
	
		
			
			| 460 | 494 |      return self;
 | 
	
		
			
			| 461 | 495 |  }
 | 
	
		
			
			| 462 | 496 |  
 | 
	
	
		
			
			|  | @@ -591,6 +625,32 @@ RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding
 | 
	
		
			
			| 591 | 625 |      [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
 | 
	
		
			
			| 592 | 626 |  }
 | 
	
		
			
			| 593 | 627 |  
 | 
	
		
			
			|  | 628 | +RCT_EXPORT_METHOD(writeStream:(NSString *)path withEncoding:(NSString *)encoding callback:(RCTResponseSenderBlock)callback) {
 | 
	
		
			
			|  | 629 | +    FetchBlobFS *fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
 | 
	
		
			
			|  | 630 | +    NSString * streamId = [fileStream openWithPath:path];
 | 
	
		
			
			|  | 631 | +    callback(@[streamId]);
 | 
	
		
			
			|  | 632 | +}
 | 
	
		
			
			|  | 633 | +
 | 
	
		
			
			|  | 634 | +RCT_EXPORT_METHOD(writeChunk:(NSString *)streamId withData:(NSString *)data encoding:(NSString *)encode callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			|  | 635 | +    FetchBlobFS *fs = [[FetchBlobFS getFileStreams] valueForKey:streamId];
 | 
	
		
			
			|  | 636 | +    NSMutableData * decodedData = [NSData alloc];
 | 
	
		
			
			|  | 637 | +    if([[encode lowercaseString] isEqualToString:@"base64"]) {
 | 
	
		
			
			|  | 638 | +        [fs write:[data dataUsingEncoding:NSUTF8StringEncoding]];
 | 
	
		
			
			|  | 639 | +    }
 | 
	
		
			
			|  | 640 | +    if([[encode lowercaseString] isEqualToString:@"utf8"]) {
 | 
	
		
			
			|  | 641 | +        [fs write:[data dataUsingEncoding:NSUTF8StringEncoding]];
 | 
	
		
			
			|  | 642 | +    }
 | 
	
		
			
			|  | 643 | +    else if([[encode lowercaseString] isEqualToString:@"ascii"]) {
 | 
	
		
			
			|  | 644 | +        [fs write:[data dataUsingEncoding:NSASCIIStringEncoding]];
 | 
	
		
			
			|  | 645 | +    }
 | 
	
		
			
			|  | 646 | +}
 | 
	
		
			
			|  | 647 | +
 | 
	
		
			
			|  | 648 | +RCT_EXPORT_METHOD(closeStream:(NSString *)streamId callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			|  | 649 | +    FetchBlobFS *fs = [[FetchBlobFS getFileStreams] valueForKey:streamId];
 | 
	
		
			
			|  | 650 | +    [fs closeOutStream];
 | 
	
		
			
			|  | 651 | +    callback(@[[NSNull null], @YES]);
 | 
	
		
			
			|  | 652 | +}
 | 
	
		
			
			|  | 653 | +
 | 
	
		
			
			| 594 | 654 |  RCT_EXPORT_METHOD(unlink:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			| 595 | 655 |      NSError * error = nil;
 | 
	
		
			
			| 596 | 656 |      NSString * tmpPath = nil;
 | 
	
	
		
			
			|  | @@ -601,6 +661,62 @@ RCT_EXPORT_METHOD(unlink:(NSString *)path callback:(RCTResponseSenderBlock) call
 | 
	
		
			
			| 601 | 661 |          callback(@[[NSString stringWithFormat:@"failed to unlink file or path at %@", path]]);
 | 
	
		
			
			| 602 | 662 |  }
 | 
	
		
			
			| 603 | 663 |  
 | 
	
		
			
			|  | 664 | +RCT_EXPORT_METHOD(removeSession:(NSArray *)paths callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			|  | 665 | +    NSError * error = nil;
 | 
	
		
			
			|  | 666 | +    NSString * tmpPath = nil;
 | 
	
		
			
			|  | 667 | +    
 | 
	
		
			
			|  | 668 | +    for(NSString * path in paths) {
 | 
	
		
			
			|  | 669 | +        [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
 | 
	
		
			
			|  | 670 | +        if(error != nil) {
 | 
	
		
			
			|  | 671 | +            callback(@[[NSString stringWithFormat:@"failed to remove session path at %@", path]]);
 | 
	
		
			
			|  | 672 | +            return;
 | 
	
		
			
			|  | 673 | +        }
 | 
	
		
			
			|  | 674 | +    }
 | 
	
		
			
			|  | 675 | +    callback(@[[NSNull null]]);
 | 
	
		
			
			|  | 676 | +    
 | 
	
		
			
			|  | 677 | +}
 | 
	
		
			
			|  | 678 | +
 | 
	
		
			
			|  | 679 | +RCT_EXPORT_METHOD(ls:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			|  | 680 | +    NSError * error = nil;
 | 
	
		
			
			|  | 681 | +    NSArray * result = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
 | 
	
		
			
			|  | 682 | +    
 | 
	
		
			
			|  | 683 | +    if(error == nil)
 | 
	
		
			
			|  | 684 | +        callback(@[[NSNull null], result == nil ? [NSNull null] :result ]);
 | 
	
		
			
			|  | 685 | +    else
 | 
	
		
			
			|  | 686 | +        callback(@[[error localizedDescription], [NSNull null]]);
 | 
	
		
			
			|  | 687 | +    
 | 
	
		
			
			|  | 688 | +}
 | 
	
		
			
			|  | 689 | +
 | 
	
		
			
			|  | 690 | +RCT_EXPORT_METHOD(cp:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			|  | 691 | +    NSError * error = nil;
 | 
	
		
			
			|  | 692 | +    BOOL result = [[NSFileManager defaultManager] copyItemAtURL:path toURL:dest error:&error];
 | 
	
		
			
			|  | 693 | +    
 | 
	
		
			
			|  | 694 | +    if(error == nil)
 | 
	
		
			
			|  | 695 | +        callback(@[[NSNull null], @YES]);
 | 
	
		
			
			|  | 696 | +    else
 | 
	
		
			
			|  | 697 | +        callback(@[[error localizedDescription], @NO]);
 | 
	
		
			
			|  | 698 | +    
 | 
	
		
			
			|  | 699 | +}
 | 
	
		
			
			|  | 700 | +
 | 
	
		
			
			|  | 701 | +RCT_EXPORT_METHOD(mv:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			|  | 702 | +    NSError * error = nil;
 | 
	
		
			
			|  | 703 | +    BOOL result = [[NSFileManager defaultManager] moveItemAtURL:path toURL:dest error:&error];
 | 
	
		
			
			|  | 704 | +    
 | 
	
		
			
			|  | 705 | +    if(error == nil)
 | 
	
		
			
			|  | 706 | +        callback(@[[NSNull null], @YES]);
 | 
	
		
			
			|  | 707 | +    else
 | 
	
		
			
			|  | 708 | +        callback(@[[error localizedDescription], @NO]);
 | 
	
		
			
			|  | 709 | +    
 | 
	
		
			
			|  | 710 | +}
 | 
	
		
			
			|  | 711 | +
 | 
	
		
			
			|  | 712 | +RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			|  | 713 | +    if([FetchBlobFS exists:path])
 | 
	
		
			
			|  | 714 | +        callback(@[@"file path exists"]);
 | 
	
		
			
			|  | 715 | +    else
 | 
	
		
			
			|  | 716 | +        [FetchBlobFS mkdir:path];
 | 
	
		
			
			|  | 717 | +    callback(@[[NSNull null]]);
 | 
	
		
			
			|  | 718 | +}
 | 
	
		
			
			|  | 719 | +
 | 
	
		
			
			| 604 | 720 |  RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {
 | 
	
		
			
			| 605 | 721 |      
 | 
	
		
			
			| 606 | 722 |      callback(@[
 |