Browse Source

Correct fs API implementation

Ben Hsieh 8 years ago
parent
commit
79865a0b3a

+ 47
- 15
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java View File

16
 import java.io.IOException;
16
 import java.io.IOException;
17
 import java.io.InputStream;
17
 import java.io.InputStream;
18
 import java.io.OutputStream;
18
 import java.io.OutputStream;
19
+import java.io.OutputStreamWriter;
19
 import java.nio.charset.Charset;
20
 import java.nio.charset.Charset;
20
 import java.nio.charset.StandardCharsets;
21
 import java.nio.charset.StandardCharsets;
21
 import java.util.HashMap;
22
 import java.util.HashMap;
163
 
164
 
164
         RNFetchBlobFS fs = fileStreams.get(streamId);
165
         RNFetchBlobFS fs = fileStreams.get(streamId);
165
         FileOutputStream stream = fs.writeStreamInstance;
166
         FileOutputStream stream = fs.writeStreamInstance;
166
-        byte [] chunk;
167
-        if(fs.encoding.equalsIgnoreCase("ascii")) {
168
-            chunk = data.getBytes(Charset.forName("US-ASCII"));
169
-        }
170
-        else if(fs.encoding.equalsIgnoreCase("base64")) {
171
-            chunk = Base64.decode(data, 0);
172
-        }
173
-        else if(fs.encoding.equalsIgnoreCase("utf8")) {
174
-            chunk = data.getBytes(Charset.forName("UTF-8"));
175
-        }
176
-        else {
177
-            chunk = data.getBytes(Charset.forName("US-ASCII"));
178
-        }
167
+        byte [] chunk = RNFetchBlobFS.stringToBytes(data, fs.encoding);
168
+
179
         try {
169
         try {
180
             stream.write(chunk);
170
             stream.write(chunk);
181
             callback.invoke(null);
171
             callback.invoke(null);
303
     static void ls(String path, Callback callback) {
293
     static void ls(String path, Callback callback) {
304
         File src = new File(path);
294
         File src = new File(path);
305
         if(!src.exists() || !src.isDirectory())
295
         if(!src.exists() || !src.isDirectory())
306
-            callback.invoke(null);
296
+            callback.invoke("failed to list path `"+path+"` for it is not exist or it is not a folder");
307
         String [] files = new File(path).list();
297
         String [] files = new File(path).list();
308
-        callback.invoke(files);
298
+        callback.invoke(null, files);
299
+    }
300
+
301
+    /**
302
+     * Create new file at path
303
+     * @param path
304
+     * @param data
305
+     * @param encoding
306
+     * @param callback
307
+     */
308
+    static void createFile(String path, String data, String encoding, Callback callback) {
309
+        try {
310
+            File dest = new File(path);
311
+            boolean created = dest.createNewFile();
312
+            if(!created) {
313
+                callback.invoke("failed to create file at path `" + path + "` for its parent path may not exists");
314
+                return;
315
+            }
316
+            OutputStream ostream = new FileOutputStream(dest);
317
+            ostream.write(RNFetchBlobFS.stringToBytes(data, encoding));
318
+            callback.invoke(null, path);
319
+        } catch(Exception err) {
320
+            callback.invoke(err.getLocalizedMessage());
321
+        }
322
+    }
323
+
324
+    /**
325
+     * String to byte converter method
326
+     * @param data  Raw data in string format
327
+     * @param encoding Decoder name
328
+     * @return  Converted data byte array
329
+     */
330
+    private static byte[] stringToBytes(String data, String encoding) {
331
+        if(encoding.equalsIgnoreCase("ascii")) {
332
+            return data.getBytes(Charset.forName("US-ASCII"));
333
+        }
334
+        else if(encoding.equalsIgnoreCase("base64")) {
335
+            return Base64.decode(data, 0);
336
+        }
337
+        else if(encoding.equalsIgnoreCase("utf8")) {
338
+            return data.getBytes(Charset.forName("UTF-8"));
339
+        }
340
+        return data.getBytes(Charset.forName("US-ASCII"));
309
     }
341
     }
310
 
342
 
311
     /**
343
     /**

+ 46
- 5
src/fs.js View File

55
   }
55
   }
56
 }
56
 }
57
 
57
 
58
+function createFile(path:string, data:string, encoding: 'base64' | 'ascii' | 'utf8'):Promise {
59
+  return new Promise((resolve, reject) => {
60
+    RNFetchBlob.createFile(path, data, encoding, (err) => {
61
+      if(err)
62
+        reject(err)
63
+      else
64
+        resolve()
65
+    })
66
+  })
67
+}
68
+
58
 /**
69
 /**
59
  * Create write stream to a file.
70
  * Create write stream to a file.
60
  * @param  {string} path Target path of file stream.
71
  * @param  {string} path Target path of file stream.
95
 
106
 
96
   if(!path)
107
   if(!path)
97
     throw Error('RNFetchBlob could not open file stream with empty `path`')
108
     throw Error('RNFetchBlob could not open file stream with empty `path`')
98
-
109
+  encoding = encoding || 'utf8'
99
   let stream:RNFetchBlobStream = {
110
   let stream:RNFetchBlobStream = {
100
     onData : function(fn) {
111
     onData : function(fn) {
101
       this._onData = fn
112
       this._onData = fn
118
       stream._onEnd(detail)
129
       stream._onEnd(detail)
119
     }
130
     }
120
     else {
131
     else {
121
-      stream._onError(detail)
132
+      if(stream._onError)
133
+        stream._onError(detail)
134
+      else
135
+        throw new Error(detail)
122
     }
136
     }
123
     // when stream closed or error, remove event handler
137
     // when stream closed or error, remove event handler
124
     if (event === 'error' || event === 'end') {
138
     if (event === 'error' || event === 'end') {
157
 
171
 
158
 function mv(path:string, dest:string):Promise<boolean> {
172
 function mv(path:string, dest:string):Promise<boolean> {
159
   return new Promise((resolve, reject) => {
173
   return new Promise((resolve, reject) => {
160
-    RNFetchBlob.ls(path, dest, (err, res) => {
174
+    RNFetchBlob.mv(path, dest, (err, res) => {
161
       if(err)
175
       if(err)
162
         reject(err)
176
         reject(err)
163
       else
177
       else
212
 
226
 
213
 }
227
 }
214
 
228
 
229
+function isDir(path:string):Promise<bool, bool> {
230
+
231
+  return new Promise((resolve, reject) => {
232
+    try {
233
+      RNFetchBlob.exists(path, (exist, isDir) => {
234
+        resolve(isDir)
235
+      })
236
+    } catch(err) {
237
+      reject(err)
238
+    }
239
+  })
240
+
241
+}
242
+
243
+
215
 /**
244
 /**
216
  * Session class
245
  * Session class
217
  * @class RNFetchBlobSession
246
  * @class RNFetchBlobSession
281
     this.append = append
310
     this.append = append
282
   }
311
   }
283
 
312
 
284
-  write() {
313
+  write(data:string) {
285
     return new Promise((resolve, reject) => {
314
     return new Promise((resolve, reject) => {
286
       try {
315
       try {
287
         RNFetchBlob.writeChunk(this.id, data, (error) => {
316
         RNFetchBlob.writeChunk(this.id, data, (error) => {
311
 }
340
 }
312
 
341
 
313
 export default {
342
 export default {
314
-  RNFetchBlobSession, unlink, mkdir, session, ls, readStream, getSystemDirs, mv, cp
343
+  RNFetchBlobSession,
344
+  unlink,
345
+  mkdir,
346
+  session,
347
+  ls,
348
+  readStream,
349
+  getSystemDirs,
350
+  mv,
351
+  cp,
352
+  writeStream,
353
+  exists,
354
+  createFile,
355
+  isDir
315
 }
356
 }

+ 8
- 1
src/index.js View File

23
   RNFetchBlobSession,
23
   RNFetchBlobSession,
24
   getSystemDirs,
24
   getSystemDirs,
25
   readStream,
25
   readStream,
26
+  createFile,
26
   unlink,
27
   unlink,
28
+  exists,
27
   mkdir,
29
   mkdir,
28
   session,
30
   session,
29
   writeStream,
31
   writeStream,
30
   ls,
32
   ls,
33
+  isDir,
31
   mv,
34
   mv,
32
   cp
35
   cp
33
 } = fs
36
 } = fs
245
 }
248
 }
246
 
249
 
247
 export default {
250
 export default {
248
-  fetch, base64, config, getSystemDirs, readStream, unlink, session, ls, mkdir, mv, cp, writeStream
251
+  fetch,
252
+  base64,
253
+  config,
254
+  session,
255
+  fs,
249
 }
256
 }

+ 77
- 31
src/ios/RNFetchBlob/RNFetchBlob.m View File

26
 NSString *const FS_EVENT_END = @"end";
26
 NSString *const FS_EVENT_END = @"end";
27
 NSString *const FS_EVENT_WARN = @"warn";
27
 NSString *const FS_EVENT_WARN = @"warn";
28
 NSString *const FS_EVENT_ERROR = @"error";
28
 NSString *const FS_EVENT_ERROR = @"error";
29
+NSMutableDictionary *fileStreams = nil;
29
 
30
 
30
 ////////////////////////////////////////
31
 ////////////////////////////////////////
31
 //
32
 //
48
 // static member getter
49
 // static member getter
49
 + (NSArray *) getFileStreams {
50
 + (NSArray *) getFileStreams {
50
     
51
     
51
-    static NSMutableData *fileStreams = nil;
52
     if(fileStreams == nil)
52
     if(fileStreams == nil)
53
-        fileStreams = [[NSArray alloc] init];
53
+        fileStreams = [[NSMutableDictionary alloc] init];
54
     return fileStreams;
54
     return fileStreams;
55
 }
55
 }
56
 
56
 
57
++(void) setFileStream:(FetchBlobFS *) instance withId:(NSString *) uuid {
58
+    if(fileStreams == nil)
59
+        fileStreams = [[NSMutableDictionary alloc] init];
60
+    [fileStreams setValue:instance forKey:uuid];
61
+}
62
+
57
 + (NSString *) getCacheDir {
63
 + (NSString *) getCacheDir {
58
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
64
     return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
59
 }
65
 }
129
     [self.outStream open];
135
     [self.outStream open];
130
     NSString *uuid = [[NSUUID UUID] UUIDString];
136
     NSString *uuid = [[NSUUID UUID] UUIDString];
131
     self.streamId = uuid;
137
     self.streamId = uuid;
132
-    [[FetchBlobFS getFileStreams] setValue:self forKey:uuid];
138
+    [FetchBlobFS setFileStream:self withId:uuid];
133
     return uuid;
139
     return uuid;
134
 }
140
 }
135
 
141
 
145
     else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) {
151
     else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) {
146
         decodedData = [chunk dataUsingEncoding:NSASCIIStringEncoding];
152
         decodedData = [chunk dataUsingEncoding:NSASCIIStringEncoding];
147
     }
153
     }
148
-    NSUInteger left = [chunk length];
154
+    NSUInteger left = [decodedData length];
149
     NSUInteger nwr = 0;
155
     NSUInteger nwr = 0;
150
     do {
156
     do {
151
         nwr = [self.outStream write:[decodedData bytes] maxLength:left];
157
         nwr = [self.outStream write:[decodedData bytes] maxLength:left];
171
     }
177
     }
172
 }
178
 }
173
 
179
 
180
+// close file write stream
181
+- (void)closeOutStream {
182
+    if(self.outStream != nil) {
183
+        [self.outStream close];
184
+        self.outStream = nil;
185
+    }
186
+
187
+}
188
+
174
 - (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize{
189
 - (void)readWithPath:(NSString *)path useEncoding:(NSString *)encoding bufferSize:(int) bufferSize{
175
     
190
     
176
     self.inStream = [[NSInputStream alloc] initWithFileAtPath:path];
191
     self.inStream = [[NSInputStream alloc] initWithFileAtPath:path];
184
     // start NSStream is a runloop
199
     // start NSStream is a runloop
185
     dispatch_async(queue, ^ {
200
     dispatch_async(queue, ^ {
186
         [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
201
         [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
187
-                             forMode:NSDefaultRunLoopMode];
202
+                            forMode:NSDefaultRunLoopMode];
188
         [inStream open];
203
         [inStream open];
189
         [[NSRunLoop currentRunLoop] run];
204
         [[NSRunLoop currentRunLoop] run];
190
-    
205
+        
191
     });
206
     });
192
 }
207
 }
193
 
208
 
194
-// close file write stream
195
-- (void)closeOutStream {
196
-    if(self.outStream != nil) {
197
-        [self.outStream close];
198
-        self.outStream = nil;
199
-    }
200
-
201
-}
202
-
203
 // close file read stream
209
 // close file read stream
204
 - (void)closeInStream {
210
 - (void)closeInStream {
205
     if(self.inStream != nil) {
211
     if(self.inStream != nil) {
245
             NSMutableData * chunkData = [[NSMutableData data] init];
251
             NSMutableData * chunkData = [[NSMutableData data] init];
246
             NSInteger chunkSize = 4096;
252
             NSInteger chunkSize = 4096;
247
             if([[self.encoding lowercaseString] isEqualToString:@"base64"])
253
             if([[self.encoding lowercaseString] isEqualToString:@"base64"])
248
-                chunkSize = 4098;
254
+                chunkSize = 4095;
249
             if(self.bufferSize > 0)
255
             if(self.bufferSize > 0)
250
                 chunkSize = self.bufferSize;
256
                 chunkSize = self.bufferSize;
251
             uint8_t buf[chunkSize];
257
             uint8_t buf[chunkSize];
257
                 [chunkData appendBytes:(const void *)buf length:len];
263
                 [chunkData appendBytes:(const void *)buf length:len];
258
                 // TODO : file read progress ?
264
                 // TODO : file read progress ?
259
                 // dispatch data event
265
                 // dispatch data event
260
-                NSString * encodedChunk;
266
+                NSString * encodedChunk = [NSString alloc];
261
                 if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
267
                 if( [[self.encoding lowercaseString] isEqualToString:@"utf8"] ) {
262
                     encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
268
                     encodedChunk = [encodedChunk initWithData:chunkData encoding:NSUTF8StringEncoding];
263
                 }
269
                 }
277
                      ];
283
                      ];
278
                     return;
284
                     return;
279
                 }
285
                 }
280
-                runOnMainQueueWithoutDeadlocking(^{
281
-                    [self.bridge.eventDispatcher
282
-                     sendDeviceEventWithName:streamEventCode
283
-                     body:@{
284
-                            @"event": FS_EVENT_DATA,
285
-                            @"detail": encodedChunk
286
-                            }
287
-                     ];
288
-                });
286
+
287
+                [self.bridge.eventDispatcher
288
+                 sendDeviceEventWithName:streamEventCode
289
+                 body:@{
290
+                        @"event": FS_EVENT_DATA,
291
+                        @"detail": encodedChunk
292
+                        }
293
+                 ];
294
+
289
             }
295
             }
290
             // end of stream
296
             // end of stream
291
             else {
297
             else {
647
     });
653
     });
648
 }
654
 }
649
 
655
 
656
+RCT_EXPORT_METHOD(createFile:(NSString *)path data:(NSString *)data encoding:(NSString *)encoding callback:(RCTResponseSenderBlock)callback) {
657
+    
658
+    NSFileManager * fm = [NSFileManager defaultManager];
659
+    NSData * fileContent = nil;
660
+    
661
+    if([[encoding lowercaseString] isEqualToString:@"utf8"]) {
662
+        fileContent = [[NSData alloc] initWithData:[data dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]];
663
+    }
664
+    else if([[encoding lowercaseString] isEqualToString:@"base64"]) {
665
+        fileContent = [[NSData alloc] initWithBase64EncodedData:data options:0];
666
+    }
667
+    else {
668
+        fileContent = [[NSData alloc] initWithData:[data dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]];
669
+    }
670
+    
671
+    BOOL success = [fm createFileAtPath:path contents:fileContent attributes:NULL];
672
+    if(success == YES)
673
+        callback(@[[NSNull null]]);
674
+    else
675
+        callback(@[[NSString stringWithFormat:@"failed to create new file at path %@ please ensure the folder exists"]]);
676
+
677
+}
678
+
679
+
650
 RCT_EXPORT_METHOD(exists:(NSString *)path callback:(RCTResponseSenderBlock)callback) {
680
 RCT_EXPORT_METHOD(exists:(NSString *)path callback:(RCTResponseSenderBlock)callback) {
651
     BOOL isDir = NO;
681
     BOOL isDir = NO;
652
     BOOL exists = NO;
682
     BOOL exists = NO;
657
 
687
 
658
 RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
688
 RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
659
     FetchBlobFS *fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
689
     FetchBlobFS *fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
690
+    if(bufferSize == nil) {
691
+        if([[encoding lowercaseString] isEqualToString:@"base64"])
692
+            bufferSize = 4095;
693
+        else
694
+            bufferSize = 4096;
695
+    }
660
     [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
696
     [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
661
 }
697
 }
662
 
698
 
664
     FetchBlobFS * fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
700
     FetchBlobFS * fileStream = [[FetchBlobFS alloc] initWithBridgeRef:self.bridge];
665
     NSFileManager * fm = [NSFileManager defaultManager];
701
     NSFileManager * fm = [NSFileManager defaultManager];
666
     BOOL isDir = nil;
702
     BOOL isDir = nil;
667
-    BOOL exist = ![fm fileExistsAtPath:path isDirectory:&isDir];
703
+    BOOL exist = [fm fileExistsAtPath:path isDirectory:&isDir];
668
     if( exist == NO || isDir == YES) {
704
     if( exist == NO || isDir == YES) {
669
         callback(@[[NSString stringWithFormat:@"target path `%@` may not exists or it's a folder", path]]);
705
         callback(@[[NSString stringWithFormat:@"target path `%@` may not exists or it's a folder", path]]);
670
         return;
706
         return;
711
 }
747
 }
712
 
748
 
713
 RCT_EXPORT_METHOD(ls:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
749
 RCT_EXPORT_METHOD(ls:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
750
+    NSFileManager* fm = [NSFileManager defaultManager];
751
+    BOOL exist = nil;
752
+    BOOL isDir = nil;
753
+    exist = [fm fileExistsAtPath:path isDirectory:&isDir];
754
+    if(exist == NO || isDir == NO) {
755
+        callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not a folder", path]]);
756
+        return ;
757
+    }
714
     NSError * error = nil;
758
     NSError * error = nil;
715
     NSArray * result = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
759
     NSArray * result = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
716
     
760
     
723
 
767
 
724
 RCT_EXPORT_METHOD(cp:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
768
 RCT_EXPORT_METHOD(cp:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
725
     NSError * error = nil;
769
     NSError * error = nil;
726
-    BOOL result = [[NSFileManager defaultManager] copyItemAtURL:path toURL:dest error:&error];
770
+    BOOL result = [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
727
     
771
     
728
     if(error == nil)
772
     if(error == nil)
729
         callback(@[[NSNull null], @YES]);
773
         callback(@[[NSNull null], @YES]);
734
 
778
 
735
 RCT_EXPORT_METHOD(mv:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
779
 RCT_EXPORT_METHOD(mv:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
736
     NSError * error = nil;
780
     NSError * error = nil;
737
-    BOOL result = [[NSFileManager defaultManager] moveItemAtURL:path toURL:dest error:&error];
781
+    BOOL result = [[NSFileManager defaultManager] moveItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
738
     
782
     
739
     if(error == nil)
783
     if(error == nil)
740
         callback(@[[NSNull null], @YES]);
784
         callback(@[[NSNull null], @YES]);
744
 }
788
 }
745
 
789
 
746
 RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
790
 RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callback) {
747
-    if([FetchBlobFS exists:path])
748
-        callback(@[@"file path exists"]);
791
+    if([FetchBlobFS exists:path]) {
792
+        callback(@[@"mkdir failed, folder already exists"]);
793
+        return;
794
+    }
749
     else
795
     else
750
         [FetchBlobFS mkdir:path];
796
         [FetchBlobFS mkdir:path];
751
     callback(@[[NSNull null]]);
797
     callback(@[[NSNull null]]);