Kaynağa Gözat

Merge remote-tracking branch 'wkh237/feat_45' into feat_45

* wkh237/feat_45:
  Add test cases #45
  Fix IOS ALAsset upload issue #45
  Add test cases for #45
  Fix cp and stat for assets #45
  Add #45 Android implementation
Erik Smartt 8 yıl önce
ebeveyn
işleme
2a7ca8d311

+ 3
- 0
src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java Dosyayı Görüntüle

@@ -13,9 +13,12 @@ import java.util.Map;
13 13
 public class RNFetchBlob extends ReactContextBaseJavaModule {
14 14
 
15 15
     String filePathPrefix = "RNFetchBlob-file://";
16
+    static ReactApplicationContext RCTContext;
16 17
 
17 18
     public RNFetchBlob(ReactApplicationContext reactContext) {
19
+
18 20
         super(reactContext);
21
+        RCTContext = reactContext;
19 22
     }
20 23
 
21 24
     @Override

+ 160
- 43
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java Dosyayı Görüntüle

@@ -1,10 +1,16 @@
1 1
 package com.RNFetchBlob;
2 2
 
3
+import android.app.Application;
4
+import android.content.CursorLoader;
3 5
 import android.content.Intent;
6
+import android.content.res.AssetFileDescriptor;
7
+import android.content.res.AssetManager;
8
+import android.database.Cursor;
4 9
 import android.media.MediaScannerConnection;
5 10
 import android.net.Uri;
6 11
 import android.os.AsyncTask;
7 12
 import android.os.Environment;
13
+import android.provider.MediaStore;
8 14
 
9 15
 import com.facebook.react.bridge.Arguments;
10 16
 import com.facebook.react.bridge.Callback;
@@ -43,6 +49,7 @@ public class RNFetchBlobFS {
43 49
     ReactApplicationContext mCtx;
44 50
     DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
45 51
     String encoding = "base64";
52
+    static final String assetPrefix = "bundle-assets://";
46 53
     boolean append = false;
47 54
     OutputStream writeStreamInstance = null;
48 55
     static HashMap<String, RNFetchBlobFS> fileStreams = new HashMap<>();
@@ -134,6 +141,7 @@ public class RNFetchBlobFS {
134 141
      * @param promise
135 142
      */
136 143
     static public void readFile(String path, String encoding, final Promise promise ) {
144
+        path = normalizePath(path);
137 145
         AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
138 146
             @Override
139 147
             protected Integer doInBackground(String... strings) {
@@ -141,12 +149,25 @@ public class RNFetchBlobFS {
141 149
                 try {
142 150
                     String path = strings[0];
143 151
                     String encoding = strings[1];
144
-                    File f = new File(path);
145
-                    int length = (int) f.length();
146
-                    byte[] bytes = new byte[length];
147
-                    FileInputStream in = new FileInputStream(f);
148
-                    in.read(bytes);
149
-                    in.close();
152
+                    byte[] bytes;
153
+
154
+                    if(path.startsWith(assetPrefix)) {
155
+                        String assetName = path.replace(assetPrefix, "");
156
+                        long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength();
157
+                        bytes = new byte[(int) length];
158
+                        InputStream in = RNFetchBlob.RCTContext.getAssets().open(assetName);
159
+                        in.read(bytes, 0, (int) length);
160
+                        in.close();
161
+                    }
162
+                    else {
163
+                        File f = new File(path);
164
+                        int length = (int) f.length();
165
+                        bytes = new byte[length];
166
+                        FileInputStream in = new FileInputStream(f);
167
+                        in.read(bytes);
168
+                        in.close();
169
+                    }
170
+
150 171
                     switch (encoding.toLowerCase()) {
151 172
                         case "base64" :
152 173
                             promise.resolve(Base64.encodeToString(bytes, 0));
@@ -209,6 +230,7 @@ public class RNFetchBlobFS {
209 230
      * @param bufferSize    Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
210 231
      */
211 232
     public void readStream( String path, String encoding, int bufferSize) {
233
+        path = normalizePath(path);
212 234
         AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
213 235
             @Override
214 236
             protected Integer doInBackground(String ... args) {
@@ -221,7 +243,15 @@ public class RNFetchBlobFS {
221 243
                     int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
222 244
                     if(bufferSize > 0)
223 245
                         chunkSize = bufferSize;
224
-                    FileInputStream fs = new FileInputStream(new File(path));
246
+
247
+                    InputStream fs;
248
+                    if(path.startsWith(assetPrefix)) {
249
+                        fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
250
+                    }
251
+                    else {
252
+                        fs = new FileInputStream(new File(path));
253
+                    }
254
+
225 255
                     byte[] buffer = new byte[chunkSize];
226 256
                     int cursor = 0;
227 257
                     boolean error = false;
@@ -314,7 +344,6 @@ public class RNFetchBlobFS {
314 344
         try {
315 345
             stream.write(chunk);
316 346
             callback.invoke();
317
-            chunk = null;
318 347
         } catch (Exception e) {
319 348
             callback.invoke(e.getLocalizedMessage());
320 349
         }
@@ -344,7 +373,7 @@ public class RNFetchBlobFS {
344 373
     }
345 374
 
346 375
     /**
347
-     * Close file stream by ID
376
+     * Close file write stream by ID
348 377
      * @param streamId Stream ID
349 378
      * @param callback JS context callback
350 379
      */
@@ -395,13 +424,13 @@ public class RNFetchBlobFS {
395 424
      * @param callback  JS context callback
396 425
      */
397 426
     static void cp(String path, String dest, Callback callback) {
427
+        path = normalizePath(path);
398 428
         InputStream in = null;
399 429
         OutputStream out = null;
400 430
 
401 431
         try {
402 432
 
403
-            String destFolder = new File(dest).getPath();
404
-            if(!new File(path).exists()) {
433
+            if(!isPathExists(path)) {
405 434
                 callback.invoke("cp error: source file at path`" + path + "` not exists");
406 435
                 return;
407 436
             }
@@ -409,7 +438,7 @@ public class RNFetchBlobFS {
409 438
             if(!new File(dest).exists())
410 439
                 new File(dest).createNewFile();
411 440
 
412
-            in = new FileInputStream(path);
441
+            in = inputStreamFromPath(path);
413 442
             out = new FileOutputStream(dest);
414 443
 
415 444
             byte[] buf = new byte[1024];
@@ -454,9 +483,22 @@ public class RNFetchBlobFS {
454 483
      * @param callback  JS context callback
455 484
      */
456 485
     static void exists(String path, Callback callback) {
457
-        boolean exist = new File(path).exists();
458
-        boolean isDir = new File(path).isDirectory();;
459
-        callback.invoke(exist, isDir);
486
+        path = normalizePath(path);
487
+        if(isAsset(path)) {
488
+            try {
489
+                String filename = path.replace(assetPrefix, "");
490
+                AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(filename);
491
+                callback.invoke(true, false);
492
+            } catch (IOException e) {
493
+                callback.invoke(false, false);
494
+            }
495
+        }
496
+        else {
497
+            boolean exist = new File(path).exists();
498
+            boolean isDir = new File(path).isDirectory();
499
+            callback.invoke(exist, isDir);
500
+        }
501
+
460 502
     }
461 503
 
462 504
     /**
@@ -465,21 +507,23 @@ public class RNFetchBlobFS {
465 507
      * @param callback  JS context callback
466 508
      */
467 509
     static void ls(String path, Callback callback) {
510
+        path = normalizePath(path);
468 511
         File src = new File(path);
469
-        if(!src.exists() || !src.isDirectory()) {
512
+        if (!src.exists() || !src.isDirectory()) {
470 513
             callback.invoke("ls error: failed to list path `" + path + "` for it is not exist or it is not a folder");
471 514
             return;
472 515
         }
473
-        String [] files = new File(path).list();
516
+        String[] files = new File(path).list();
474 517
         WritableArray arg = Arguments.createArray();
475
-        for(String i : files) {
518
+        for (String i : files) {
476 519
             arg.pushString(i);
477 520
         }
478 521
         callback.invoke(null, arg);
479 522
     }
480 523
 
481 524
     static void lstat(String path, final Callback callback) {
482
-        File src = new File(path);
525
+        path = normalizePath(path);
526
+
483 527
         new AsyncTask<String, Integer, Integer>() {
484 528
             @Override
485 529
             protected Integer doInBackground(String ...args) {
@@ -511,24 +555,55 @@ public class RNFetchBlobFS {
511 555
      */
512 556
     static void stat(String path, Callback callback) {
513 557
         try {
514
-            File target = new File(path);
515
-            if (!target.exists()) {
516
-                callback.invoke("stat error: file " + path + " does not exists");
517
-                return;
518
-            }
519
-            WritableMap stat = Arguments.createMap();
520
-            stat.putString("filename", target.getName());
521
-            stat.putString("path", target.getPath());
522
-            stat.putString("type", target.isDirectory() ? "directory" : "file");
523
-            stat.putString("size", String.valueOf(target.length()));
524
-            String lastModified = String.valueOf(target.lastModified());
525
-            stat.putString("lastModified", lastModified);
526
-            callback.invoke(null, stat);
558
+            callback.invoke(null, statFile(path));
527 559
         } catch(Exception err) {
528 560
             callback.invoke(err.getLocalizedMessage());
529 561
         }
530 562
     }
531 563
 
564
+    /**
565
+     * Basic stat method
566
+     * @param path
567
+     * @return Stat result of a file or path
568
+     */
569
+    static WritableMap statFile(String path) {
570
+        try {
571
+            path = normalizePath(path);
572
+            WritableMap stat = Arguments.createMap();
573
+            if(isAsset(path)) {
574
+                String name = path.replace(assetPrefix, "");
575
+                AssetFileDescriptor fd = RNFetchBlob.RCTContext.getAssets().openFd(name);
576
+                stat.putString("filename", name);
577
+                stat.putString("path", path);
578
+                stat.putString("type", "asset");
579
+                stat.putString("size", String.valueOf(fd.getLength()));
580
+                stat.putString("lastModified", "0");
581
+            }
582
+            else {
583
+                File target = new File(path);
584
+                if (!target.exists()) {
585
+                    return null;
586
+                }
587
+                stat.putString("filename", target.getName());
588
+                stat.putString("path", target.getPath());
589
+                stat.putString("type", target.isDirectory() ? "directory" : "file");
590
+                stat.putString("size", String.valueOf(target.length()));
591
+                String lastModified = String.valueOf(target.lastModified());
592
+                stat.putString("lastModified", lastModified);
593
+
594
+            }
595
+            return stat;
596
+        } catch(Exception err) {
597
+            return null;
598
+        }
599
+    }
600
+
601
+    /**
602
+     * Media scanner scan file
603
+     * @param path
604
+     * @param mimes
605
+     * @param callback
606
+     */
532 607
     void scanFile(String [] path, String[] mimes, final Callback callback) {
533 608
         try {
534 609
             MediaScannerConnection.scanFile(mCtx, path, mimes, new MediaScannerConnection.OnScanCompletedListener() {
@@ -669,18 +744,60 @@ public class RNFetchBlobFS {
669 744
         this.emitter.emit("RNFetchBlobStream" + taskId, eventData);
670 745
     }
671 746
 
672
-    static WritableMap statFile(String path) {
673
-        File target = new File(path);
674
-        if(!target.exists()) {
675
-            return null;
747
+    /**
748
+     * Get input stream of the given path, when the path is a string starts with bundle-assets://
749
+     * the stream is created by Assets Manager, otherwise use FileInputStream.
750
+     * @param path
751
+     * @return
752
+     * @throws IOException
753
+     */
754
+    static InputStream inputStreamFromPath(String path) throws IOException {
755
+        if (path.startsWith(assetPrefix)) {
756
+            return RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
757
+        }
758
+        return new FileInputStream(new File(path));
759
+    }
760
+
761
+    /**
762
+     * Check if the asset or the file exists
763
+     * @param path
764
+     * @return
765
+     */
766
+    static boolean isPathExists(String path) {
767
+        if(path.startsWith(assetPrefix)) {
768
+            try {
769
+                RNFetchBlob.RCTContext.getAssets().open(path.replace(assetPrefix, ""));
770
+            } catch (IOException e) {
771
+                return false;
772
+            }
773
+            return true;
774
+        }
775
+        else {
776
+            return new File(path).exists();
777
+        }
778
+
779
+    }
780
+
781
+    static boolean isAsset(String path) {
782
+        return path.startsWith(assetPrefix);
783
+    }
784
+
785
+    static String normalizePath(String path) {
786
+        if(path.startsWith("bundle-assets://")) {
787
+            return path;
788
+        }
789
+        else if (path.startsWith("content://")) {
790
+            String[] proj = { MediaStore.Images.Media.DATA };
791
+            Uri contentUri = Uri.parse(path);
792
+            CursorLoader loader = new CursorLoader(RNFetchBlob.RCTContext, contentUri, proj, null, null, null);
793
+            Cursor cursor = loader.loadInBackground();
794
+            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
795
+            cursor.moveToFirst();
796
+            String result = cursor.getString(column_index);
797
+            cursor.close();
798
+            return result;
676 799
         }
677
-        WritableMap stat = Arguments.createMap();
678
-        stat.putString("filename", target.getName());
679
-        stat.putString("path", target.getPath());
680
-        stat.putString("type", target.isDirectory() ? "directory" : "file");
681
-        stat.putInt("size", (int)target.length());
682
-        stat.putInt("lastModified", (int)target.lastModified());
683
-        return stat;
800
+        return path;
684 801
     }
685 802
 
686 803
 }

+ 12
- 1
src/fs.js Dosyayı Görüntüle

@@ -8,6 +8,7 @@
8 8
 import {
9 9
   NativeModules,
10 10
   DeviceEventEmitter,
11
+  Platform,
11 12
   NativeAppEventEmitter,
12 13
 } from 'react-native'
13 14
 import RNFetchBlobSession from './class/RNFetchBlobSession'
@@ -47,6 +48,15 @@ function session(name:string):RNFetchBlobSession {
47 48
   }
48 49
 }
49 50
 
51
+function asset(path:string):string {
52
+  if(Platform.OS === 'ios') {
53
+    // path from camera roll
54
+    if(/^assets-library\:\/\//.test(path))
55
+      return path
56
+  }
57
+  return 'bundle-assets://' + path
58
+}
59
+
50 60
 function createFile(path:string, data:string, encoding: 'base64' | 'ascii' | 'utf8'):Promise {
51 61
   encoding = encoding || 'utf8'
52 62
   return new Promise((resolve, reject) => {
@@ -324,5 +334,6 @@ export default {
324 334
   stat,
325 335
   lstat,
326 336
   scanFile,
327
-  dirs
337
+  dirs,
338
+  asset
328 339
 }

+ 1
- 1
src/index.js Dosyayı Görüntüle

@@ -272,5 +272,5 @@ export default {
272 272
   config,
273 273
   session,
274 274
   fs,
275
-  wrap,
275
+  wrap
276 276
 }

+ 6
- 0
src/ios/RNFetchBlob.xcodeproj/project.pbxproj Dosyayı Görüntüle

@@ -12,6 +12,7 @@
12 12
 		A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; };
13 13
 		A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = A15C30131CD25C330074CB35 /* RNFetchBlob.m */; };
14 14
 		A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */ = {isa = PBXBuildFile; fileRef = A15C30111CD25C330074CB35 /* RNFetchBlob.h */; };
15
+		A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */; };
15 16
 /* End PBXBuildFile section */
16 17
 
17 18
 /* Begin PBXCopyFilesBuildPhase section */
@@ -36,6 +37,8 @@
36 37
 		A15C300E1CD25C330074CB35 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFetchBlob.a; sourceTree = BUILT_PRODUCTS_DIR; };
37 38
 		A15C30111CD25C330074CB35 /* RNFetchBlob.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNFetchBlob.h; path = RNFetchBlob/RNFetchBlob.h; sourceTree = "<group>"; };
38 39
 		A15C30131CD25C330074CB35 /* RNFetchBlob.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RNFetchBlob.m; path = RNFetchBlob/RNFetchBlob.m; sourceTree = "<group>"; };
40
+		A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobReqBuilder.h; sourceTree = "<group>"; };
41
+		A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobReqBuilder.m; sourceTree = "<group>"; };
39 42
 /* End PBXFileReference section */
40 43
 
41 44
 /* Begin PBXFrameworksBuildPhase section */
@@ -59,6 +62,8 @@
59 62
 		A15C30051CD25C330074CB35 = {
60 63
 			isa = PBXGroup;
61 64
 			children = (
65
+				A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */,
66
+				A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */,
62 67
 				A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */,
63 68
 				A158F42E1D0539CE006FFD38 /* RNFetchBlobNetwork.h */,
64 69
 				A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */,
@@ -140,6 +145,7 @@
140 145
 				A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */,
141 146
 				A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */,
142 147
 				A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */,
148
+				A1AAE2991D300E4D0051D11C /* RNFetchBlobReqBuilder.m in Sources */,
143 149
 				A15C30141CD25C330074CB35 /* RNFetchBlob.m in Sources */,
144 150
 			);
145 151
 			runOnlyForDeploymentPostprocessing = 0;

+ 18
- 112
src/ios/RNFetchBlob/RNFetchBlob.m Dosyayı Görüntüle

@@ -12,6 +12,7 @@
12 12
 #import "RNFetchBlobFS.h"
13 13
 #import "RNFetchBlobNetwork.h"
14 14
 #import "RNFetchBlobConst.h"
15
+#import "RNFetchBlobReqBuilder.h"
15 16
 
16 17
 
17 18
 ////////////////////////////////////////
@@ -61,77 +62,14 @@ RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options
61 62
                   form:(NSArray *)form
62 63
                   callback:(RCTResponseSenderBlock)callback)
63 64
 {
64
-    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
65
-    // send request
66
-    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
67
-                                    initWithURL:[NSURL
68
-                                                 URLWithString: encodedUrl]];
69 65
     
70
-    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[ RNFetchBlobNetwork normalizeHeaders:headers]];
71
-    
72
-    
73
-    NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
74
-    NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
75
-    
76
-    // generate boundary
77
-    NSString * boundary = [NSString stringWithFormat:@"RNFetchBlob%d", timeStampObj];
78
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
79
-        NSMutableData * postData = [[NSMutableData alloc] init];
80
-        // if method is POST or PUT, convert data string format
81
-        if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"]) {
82
-            
83
-            // combine multipart/form-data body
84
-            for(id field in form) {
85
-                NSString * name = [field valueForKey:@"name"];
86
-                NSString * content = [field valueForKey:@"data"];
87
-                // field is a text field
88
-                if([field valueForKey:@"filename"] == nil || content == [NSNull null]) {
89
-                    [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
90
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", name] dataUsingEncoding:NSUTF8StringEncoding]];
91
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
92
-                    [postData appendData:[[NSString stringWithFormat:@"%@\r\n", content] dataUsingEncoding:NSUTF8StringEncoding]];
93
-                }
94
-                // field contains a file
95
-                else {
96
-                    NSMutableData * blobData;
97
-                    if(content != nil) {
98
-                        if([content hasPrefix:self.filePathPrefix]) {
99
-                            NSString * orgPath = [content substringFromIndex:[self.filePathPrefix length]];
100
-                            blobData = [[NSData alloc] initWithContentsOfFile:orgPath];
101
-                        }
102
-                        else
103
-                            blobData = [[NSData alloc] initWithBase64EncodedString:content options:0];
104
-                    }
105
-                    NSString * filename = [field valueForKey:@"filename"];
106
-                    [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
107
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
108
-                    [postData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
109
-                    [postData appendData:blobData];
110
-                    [postData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
111
-                }
112
-                
113
-            }
114
-            
115
-            // close form data
116
-            [postData appendData: [[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
117
-            [request setHTTPBody:postData];
118
-            // set content-length
119
-            [mheaders setValue:[NSString stringWithFormat:@"%d",[postData length]] forKey:@"Content-Length"];
120
-            [mheaders setValue:[NSString stringWithFormat:@"100-continue",[postData length]] forKey:@"Expect"];
121
-            // appaned boundary to content-type
122
-            [mheaders setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forKey:@"content-type"];
123
-            
124
-        }
125
-        
126
-        [request setHTTPMethod: method];
127
-        [request setAllHTTPHeaderFields:mheaders];
128
-        
129
-        
66
+    [RNFetchBlobReqBuilder buildMultipartRequest:options taskId:taskId method:method url:url headers:headers form:form onComplete:^(NSURLRequest *req) {
130 67
         // send HTTP request
131 68
         RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
132
-        [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:request callback:callback];
69
+        [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:req callback:callback];
133 70
         utils = nil;
134
-    });
71
+    }];
72
+    
135 73
 }
136 74
 
137 75
 // Fetch blob data request
@@ -142,45 +80,13 @@ RCT_EXPORT_METHOD(fetchBlob:(NSDictionary *)options
142 80
                   headers:(NSDictionary *)headers
143 81
                   body:(NSString *)body callback:(RCTResponseSenderBlock)callback)
144 82
 {
145
-    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
146
-    // send request
147
-    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
148
-                                    initWithURL:[NSURL
149
-                                                 URLWithString: encodedUrl]];
150
-    
151
-    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
152
-    // move heavy task to another thread
153
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
154
-        NSMutableData * blobData;
155
-        // if method is POST or PUT, convert data string format
156
-        if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"]) {
157
-            // generate octet-stream body
158
-            if(body != nil) {
159
-                
160
-                // when body is a string contains file path prefix, try load file from the path
161
-                if([body hasPrefix:self.filePathPrefix]) {
162
-                    NSString * orgPath = [body substringFromIndex:[self.filePathPrefix length]];
163
-                    [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
164
-                }
165
-                // otherwise convert it as BASE64 data string
166
-                else {
167
-                    blobData = [[NSData alloc] initWithBase64EncodedString:body options:0];
168
-                    [request setHTTPBody:blobData];
169
-                }
170
-                
171
-                [mheaders setValue:@"application/octet-stream" forKey:@"content-type"];
172
-                
173
-            }
174
-        }
175
-        
176
-        [request setHTTPMethod: method];
177
-        [request setAllHTTPHeaderFields:mheaders];
178
-        
83
+    [RNFetchBlobReqBuilder buildOctetRequest:options taskId:taskId method:method url:url headers:headers body:body onComplete:^(NSURLRequest *req) {
179 84
         // send HTTP request
180 85
         RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
181
-        [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:request callback:callback];
86
+        [utils sendRequest:options bridge:self.bridge taskId:taskId withRequest:req callback:callback];
182 87
         utils = nil;
183
-    });
88
+    }];
89
+    
184 90
 }
185 91
 
186 92
 RCT_EXPORT_METHOD(createFile:(NSString *)path data:(NSString *)data encoding:(NSString *)encoding callback:(RCTResponseSenderBlock)callback) {
@@ -332,6 +238,9 @@ RCT_EXPORT_METHOD(stat:(NSString *)path callback:(RCTResponseSenderBlock) callba
332 238
     BOOL exist = nil;
333 239
     BOOL isDir = nil;
334 240
     NSError * error = nil;
241
+    
242
+    path = [RNFetchBlobFS getPathOfAsset:path];
243
+    
335 244
     exist = [fm fileExistsAtPath:path isDirectory:&isDir];
336 245
     if(exist == NO) {
337 246
         callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
@@ -350,6 +259,9 @@ RCT_EXPORT_METHOD(lstat:(NSString *)path callback:(RCTResponseSenderBlock) callb
350 259
     NSFileManager* fm = [NSFileManager defaultManager];
351 260
     BOOL exist = nil;
352 261
     BOOL isDir = nil;
262
+    
263
+    path = [RNFetchBlobFS getPathOfAsset:path];
264
+    
353 265
     exist = [fm fileExistsAtPath:path isDirectory:&isDir];
354 266
     if(exist == NO) {
355 267
         callback(@[[NSString stringWithFormat:@"failed to list path `%@` for it is not exist or it is not exist", path]]);
@@ -378,6 +290,7 @@ RCT_EXPORT_METHOD(lstat:(NSString *)path callback:(RCTResponseSenderBlock) callb
378 290
 
379 291
 RCT_EXPORT_METHOD(cp:(NSString *)path toPath:(NSString *)dest callback:(RCTResponseSenderBlock) callback) {
380 292
     NSError * error = nil;
293
+    path = [RNFetchBlobFS getPathOfAsset:path];
381 294
     BOOL result = [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:path] toURL:[NSURL fileURLWithPath:dest] error:&error];
382 295
     
383 296
     if(error == nil)
@@ -410,7 +323,7 @@ RCT_EXPORT_METHOD(mkdir:(NSString *)path callback:(RCTResponseSenderBlock) callb
410 323
 
411 324
 RCT_EXPORT_METHOD(readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
412 325
     
413
-    [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject];
326
+    [RNFetchBlobFS readFile:path encoding:encoding resolver:resolve rejecter:reject onComplete:nil];
414 327
 })
415 328
 
416 329
 RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding bufferSize:(int)bufferSize) {
@@ -423,14 +336,7 @@ RCT_EXPORT_METHOD(readStream:(NSString *)path withEncoding:(NSString *)encoding
423 336
             bufferSize = 4096;
424 337
     }
425 338
     // read asset stream
426
-    if([path hasPrefix:@"assets-library://"])
427
-    {
428
-        
429
-    }
430
-    else
431
-    {
432
-        [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
433
-    }
339
+    [fileStream readWithPath:path useEncoding:encoding bufferSize:bufferSize];
434 340
 }
435 341
 
436 342
 RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback) {

+ 3
- 0
src/ios/RNFetchBlobConst.h Dosyayı Görüntüle

@@ -16,7 +16,10 @@ extern NSString *const MSG_EVENT;
16 16
 extern NSString *const MSG_EVENT_LOG;
17 17
 extern NSString *const MSG_EVENT_WARN;
18 18
 extern NSString *const MSG_EVENT_ERROR;
19
+
19 20
 extern NSString *const FILE_PREFIX;
21
+extern NSString *const ASSET_PREFIX;
22
+extern NSString *const AL_PREFIX;
20 23
 
21 24
 // config
22 25
 extern NSString *const CONFIG_USE_TEMP;

+ 4
- 0
src/ios/RNFetchBlobConst.m Dosyayı Görüntüle

@@ -8,6 +8,10 @@
8 8
 #import "RNFetchBlobConst.h"
9 9
 
10 10
 extern NSString *const FILE_PREFIX = @"RNFetchBlob-file://";
11
+extern NSString *const ASSET_PREFIX = @"bundle-assets://";
12
+extern NSString *const AL_PREFIX = @"assets-library://";
13
+
14
+
11 15
 
12 16
 // fetch configs
13 17
 extern NSString *const CONFIG_USE_TEMP = @"fileCache";

+ 4
- 2
src/ios/RNFetchBlobFS.h Dosyayı Görüntüle

@@ -11,6 +11,7 @@
11 11
 
12 12
 #import <Foundation/Foundation.h>
13 13
 #import "RCTBridgeModule.h"
14
+@import AssetsLibrary;
14 15
 
15 16
 @interface RNFetchBlobFS : NSObject <NSStreamDelegate>  {
16 17
     NSOutputStream * outStream;
@@ -42,6 +43,8 @@
42 43
 + (NSString *) getCacheDir;
43 44
 + (NSString *) getDocumentDir;
44 45
 + (NSString *) getTempPath:(NSString*)taskId withExtension:(NSString *)ext;
46
++ (NSString *) getPathOfAsset:(NSString *)assetURI;
47
++ (void) getPathFromUri:(NSString *)uri completionHandler:(void(^)(NSString * path, ALAssetRepresentation *asset)) onComplete;
45 48
 
46 49
 // fs methods
47 50
 + (RNFetchBlobFS *) getFileStreams;
@@ -50,9 +53,8 @@
50 53
 + (BOOL) exists:(NSString *) path;
51 54
 + (void) writeFileArray:(NSString *)path data:(NSArray *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
52 55
 + (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;
56
++ (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject onComplete:(void (^)(NSData * content))onComplete;
54 57
 + (void) readAssetFile:(NSData *)assetUrl completionBlock:(void(^)(NSData * content))completionBlock failBlock:(void(^)(NSError * err))failBlock;
55
-+ (NSString *) getPathOfAsset:(NSString *)assetURI;
56 58
 
57 59
 // constructor
58 60
 - (id) init;

+ 72
- 85
src/ios/RNFetchBlobFS.m Dosyayı Görüntüle

@@ -16,6 +16,7 @@
16 16
 #import "RNFetchBlobConst.h"
17 17
 @import AssetsLibrary;
18 18
 
19
+
19 20
 NSMutableDictionary *fileStreams = nil;
20 21
 
21 22
 ////////////////////////////////////////
@@ -53,9 +54,9 @@ NSMutableDictionary *fileStreams = nil;
53 54
 +(NSString *) getPathOfAsset:(NSString *)assetURI
54 55
 {
55 56
     // get file path of an app asset
56
-    if([assetURI hasPrefix:@"bundle-assets://"])
57
+    if([assetURI hasPrefix:ASSET_PREFIX])
57 58
     {
58
-        assetURI = [assetURI stringByReplacingOccurrencesOfString:@"bundle-assets://" withString:@""];
59
+        assetURI = [assetURI stringByReplacingOccurrencesOfString:ASSET_PREFIX withString:@""];
59 60
         assetURI = [[NSBundle mainBundle] pathForResource: [assetURI stringByDeletingPathExtension]
60 61
                                                ofType: [assetURI pathExtension]];
61 62
     }
@@ -97,7 +98,7 @@ NSMutableDictionary *fileStreams = nil;
97 98
     return tempPath;
98 99
 }
99 100
 
100
-- (void) startAssetReadStream:(NSData *)assetUrl
101
+- (void) startAssetReadStream:(NSString *)assetUrl
101 102
 {
102 103
     ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
103 104
     {
@@ -184,34 +185,6 @@ NSMutableDictionary *fileStreams = nil;
184 185
     }
185 186
 }
186 187
 
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
-
215 188
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
216 189
     @try {
217 190
         NSFileManager * fm = [NSFileManager defaultManager];
@@ -300,68 +273,62 @@ NSMutableDictionary *fileStreams = nil;
300 273
     }
301 274
 }
302 275
 
303
-+ (void) readFile:(NSString *)path encoding:(NSString *)encoding resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
276
++ (void) readFile:(NSString *)path encoding:(NSString *)encoding
277
+         resolver:(RCTPromiseResolveBlock)resolve
278
+         rejecter:(RCTPromiseRejectBlock)reject
279
+       onComplete:(void (^)(NSData * content))onComplete
280
+{
304 281
     @try
305 282
     {
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)
283
+        [[self class] getPathFromUri:path completionHandler:^(NSString *path, ALAssetRepresentation *asset) {
284
+            NSData * fileContent;
285
+            NSError * err;
286
+            if(asset != nil)
287
+            {
288
+                Byte * buffer = malloc(asset.size);
289
+                [asset getBytes:buffer fromOffset:0 length:asset.size error:&err];
290
+                if(err != nil)
291
+                {
292
+                    reject(@"RNFetchBlobFS readFile error", @"failed to read asset", [err localizedDescription]);
293
+                    return;
294
+                }
295
+                fileContent = [NSData dataWithBytes:buffer length:asset.size];
296
+                if(onComplete != nil)
297
+                    onComplete(fileContent);
298
+                free(buffer);
299
+            }
300
+            else
313 301
             {
314
-                if([[encoding lowercaseString] isEqualToString:@"utf8"]) {
315
-                    resolve([[NSString alloc] initWithData:content encoding:NSUTF8StringEncoding]);
302
+                BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path];
303
+                if(!exists) {
304
+                    reject(@"RNFetchBlobFS readFile error", @"file not exists", path);
305
+                    return;
316 306
                 }
317
-                else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
318
-                    resolve([content base64EncodedStringWithOptions:0]);
307
+                fileContent = [NSData dataWithContentsOfFile:path];
308
+            }
309
+            
310
+            if([[encoding lowercaseString] isEqualToString:@"utf8"]) {
311
+                if(resolve != nil)
312
+                    resolve([[NSString alloc] initWithData:fileContent encoding:NSUTF8StringEncoding]);
313
+            }
314
+            else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
315
+                if(resolve != nil)
316
+                    resolve([fileContent base64EncodedStringWithOptions:0]);
317
+            }
318
+            else if ([[encoding lowercaseString] isEqualToString:@"ascii"]) {
319
+                NSMutableArray * resultArray = [NSMutableArray array];
320
+                char * bytes = [fileContent bytes];
321
+                for(int i=0;i<[fileContent length];i++) {
322
+                    [resultArray addObject:[NSNumber numberWithChar:bytes[i]]];
319 323
                 }
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
-                    }
324
+                if(resolve != nil)
326 325
                     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
-        
337
-        NSFileManager * fm = [NSFileManager defaultManager];
338
-        NSError *err = nil;
339
-        BOOL exists = [fm fileExistsAtPath:path];
340
-        if(!exists) {
341
-            @throw @"RNFetchBlobFS readFile error", @"file not exists", path;
342
-            return;
343
-        }
344
-        if([[encoding lowercaseString] isEqualToString:@"utf8"]) {
345
-            NSString * utf8Result = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err];
346
-            resolve(utf8Result);
347
-        }
348
-        else if ([[encoding lowercaseString] isEqualToString:@"base64"]) {
349
-            NSData * fileData = [NSData dataWithContentsOfFile:path];
350
-            resolve([fileData base64EncodedStringWithOptions:0]);
351
-        }
352
-        else if ([[encoding lowercaseString] isEqualToString:@"ascii"]) {
353
-            NSData * resultData = [NSData dataWithContentsOfFile:path];
354
-            NSMutableArray * resultArray = [NSMutableArray array];
355
-            char * bytes = [resultData bytes];
356
-            for(int i=0;i<[resultData length];i++) {
357
-                [resultArray addObject:[NSNumber numberWithChar:bytes[i]]];
358 326
             }
359
-            resolve(resultArray);
360
-        }
361
-
327
+        }];
362 328
     }
363 329
     @catch(NSException * e)
364 330
     {
331
+        if(reject != nil)
365 332
         reject(@"RNFetchBlobFS readFile error", @"error", [e description]);
366 333
     }
367 334
 }
@@ -485,9 +452,9 @@ NSMutableDictionary *fileStreams = nil;
485 452
     self.path = path;
486 453
     self.bufferSize = bufferSize;
487 454
     
488
-    if([path hasPrefix:@"assets-library://"])
455
+    if([path hasPrefix:AL_PREFIX])
489 456
     {
490
-     
457
+        [self startAssetReadStream:path];
491 458
         return;
492 459
     }
493 460
     
@@ -630,4 +597,24 @@ NSMutableDictionary *fileStreams = nil;
630 597
 
631 598
 }
632 599
 
600
++ (void) getPathFromUri:(NSString *)uri completionHandler:(void(^)(NSString * path, ALAssetRepresentation *asset)) onComplete
601
+{
602
+    if([uri hasPrefix:AL_PREFIX])
603
+    {
604
+        NSURL *asseturl = [NSURL URLWithString:uri];
605
+        ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
606
+        [assetslibrary assetForURL:asseturl
607
+                       resultBlock:^(ALAsset *asset) {
608
+                           onComplete(nil, [asset defaultRepresentation]);
609
+                       }
610
+                      failureBlock:^(NSError *error) {
611
+                          onComplete(nil, nil);
612
+                      }];
613
+    }
614
+    else
615
+    {
616
+        onComplete([[self class] getPathOfAsset:uri], nil);
617
+    }
618
+}
619
+
633 620
 @end

+ 1
- 0
src/ios/RNFetchBlobNetwork.h Dosyayı Görüntüle

@@ -11,6 +11,7 @@
11 11
 
12 12
 #import <Foundation/Foundation.h>
13 13
 #import "RCTBridgeModule.h"
14
+#import "RNFetchBlobFS.h"
14 15
 
15 16
 typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error);
16 17
 typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error);

+ 34
- 0
src/ios/RNFetchBlobReqBuilder.h Dosyayı Görüntüle

@@ -0,0 +1,34 @@
1
+//
2
+//  RNFetchBlobReqBuilder.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/7/9.
6
+//  Copyright © 2016年 wkh237. All rights reserved.
7
+//
8
+
9
+#ifndef RNFetchBlobReqBuilder_h
10
+#define RNFetchBlobReqBuilder_h
11
+
12
+#import <Foundation/Foundation.h>
13
+
14
+@interface RNFetchBlobReqBuilder : NSObject;
15
+
16
++(void) buildMultipartRequest:(NSDictionary *)options
17
+                       taskId:(NSString *)taskId
18
+                       method:(NSString *)method
19
+                          url:(NSString *)url
20
+                      headers:(NSDictionary *)headers
21
+                         form:(NSArray *)form
22
+                   onComplete:(void(^)(NSURLRequest * req))onComplete;
23
+
24
++(void) buildOctetRequest:(NSDictionary *)options
25
+                   taskId:(NSString *)taskId
26
+                   method:(NSString *)method
27
+                      url:(NSString *)url
28
+                  headers:(NSDictionary *)headers
29
+                     body:(NSString *)body
30
+               onComplete:(void(^)(NSURLRequest * req))onComplete;
31
+
32
+@end
33
+
34
+#endif /* RNFetchBlobReqBuilder_h */

+ 195
- 0
src/ios/RNFetchBlobReqBuilder.m Dosyayı Görüntüle

@@ -0,0 +1,195 @@
1
+//
2
+//  RNFetchBlobReqBuilder.m
3
+//  RNFetchBlob
4
+//
5
+//  Created by Ben Hsieh on 2016/7/9.
6
+//  Copyright © 2016年 wkh237. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "RNFetchBlobReqBuilder.h"
11
+#import "RNFetchBlobNetwork.h"
12
+#import "RNFetchBlobConst.h"
13
+#import "RNFetchBlobFS.h"
14
+
15
+@interface RNFetchBlobReqBuilder()
16
+{
17
+    
18
+}
19
+@end
20
+
21
+@implementation RNFetchBlobReqBuilder
22
+
23
+
24
+// Fetch blob data request
25
++(void) buildMultipartRequest:(NSDictionary *)options
26
+                       taskId:(NSString *)taskId
27
+                       method:(NSString *)method
28
+                          url:(NSString *)url
29
+                      headers:(NSDictionary *)headers
30
+                         form:(NSArray *)form
31
+                   onComplete:(void(^)(NSURLRequest * req))onComplete
32
+{
33
+    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
34
+    // send request
35
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString: encodedUrl]];
36
+    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
37
+    NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
38
+    NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
39
+    
40
+    // generate boundary
41
+    NSString * boundary = [NSString stringWithFormat:@"RNFetchBlob%d", timeStampObj];
42
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
43
+        NSMutableData * postData = [[NSMutableData alloc] init];
44
+        // combine multipart/form-data body
45
+        [[self class] buildFormBody:form boundary:boundary onComplete:^(NSData *formData) {
46
+            if(formData != nil) {
47
+                [postData appendData:formData];
48
+                // close form data
49
+                [postData appendData: [[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
50
+                [request setHTTPBody:postData];
51
+            }
52
+            // set content-length
53
+            [mheaders setValue:[NSString stringWithFormat:@"%d",[postData length]] forKey:@"Content-Length"];
54
+            [mheaders setValue:[NSString stringWithFormat:@"100-continue",[postData length]] forKey:@"Expect"];
55
+            // appaned boundary to content-type
56
+            [mheaders setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary] forKey:@"content-type"];
57
+            [request setHTTPMethod: method];
58
+            [request setAllHTTPHeaderFields:mheaders];
59
+            onComplete(request);
60
+        }];
61
+        
62
+    });
63
+}
64
+
65
+// Fetch blob data request
66
++(void) buildOctetRequest:(NSDictionary *)options
67
+                   taskId:(NSString *)taskId
68
+                   method:(NSString *)method
69
+                      url:(NSString *)url
70
+                  headers:(NSDictionary *)headers
71
+                     body:(NSString *)body
72
+               onComplete:(void(^)(NSURLRequest * req))onComplete
73
+{
74
+    NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
75
+    // send request
76
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
77
+                                    initWithURL:[NSURL
78
+                                                 URLWithString: encodedUrl]];
79
+    
80
+    NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[RNFetchBlobNetwork normalizeHeaders:headers]];
81
+    // move heavy task to another thread
82
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
83
+        NSMutableData * blobData;
84
+        // if method is POST or PUT, convert data string format
85
+        if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"]) {
86
+            // generate octet-stream body
87
+            if(body != nil) {
88
+                
89
+                // when body is a string contains file path prefix, try load file from the path
90
+                if([body hasPrefix:FILE_PREFIX]) {
91
+                    NSString * orgPath = [body substringFromIndex:[FILE_PREFIX length]];
92
+                    orgPath = [RNFetchBlobFS getPathOfAsset:orgPath];
93
+                    if([orgPath hasPrefix:AL_PREFIX])
94
+                    {
95
+                        [RNFetchBlobFS readFile:orgPath encoding:@"utf8" resolver:nil rejecter:nil onComplete:^(NSData *content) {
96
+                            [request setHTTPBody:content];
97
+                            [mheaders setValue:@"application/octet-stream" forKey:@"content-type"];
98
+                            [request setHTTPMethod: method];
99
+                            [request setAllHTTPHeaderFields:mheaders];
100
+                            onComplete(request);
101
+                        }];
102
+                        return;
103
+                    }
104
+                    [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
105
+                }
106
+                // otherwise convert it as BASE64 data string
107
+                else {
108
+                    blobData = [[NSData alloc] initWithBase64EncodedString:body options:0];
109
+                    [request setHTTPBody:blobData];
110
+                }
111
+                
112
+                [mheaders setValue:@"application/octet-stream" forKey:@"content-type"];
113
+                
114
+            }
115
+        }
116
+        
117
+        [request setHTTPMethod: method];
118
+        [request setAllHTTPHeaderFields:mheaders];
119
+        
120
+        onComplete(request);
121
+    });
122
+}
123
+
124
++(void) buildFormBody:(NSArray *)form boundary:(NSString *)boundary onComplete:(void(^)(NSData * formData))onComplete
125
+{
126
+    NSMutableData * formData = [[NSMutableData alloc] init];
127
+    if(form == nil)
128
+        onComplete(nil);
129
+    else
130
+    {
131
+        __block int i = 0;
132
+        __block int count = [form count];
133
+        void __block (^getFieldData)(id field) = ^(id field)
134
+        {
135
+            NSString * name = [field valueForKey:@"name"];
136
+            NSString * content = [field valueForKey:@"data"];
137
+            // field is a text field
138
+            if([field valueForKey:@"filename"] == nil || content == [NSNull null]) {
139
+                [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
140
+                [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", name] dataUsingEncoding:NSUTF8StringEncoding]];
141
+                [formData appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
142
+                [formData appendData:[[NSString stringWithFormat:@"%@\r\n", content] dataUsingEncoding:NSUTF8StringEncoding]];
143
+            }
144
+            // field contains a file
145
+            else {
146
+                NSMutableData * blobData;
147
+                if(content != nil)
148
+                {
149
+                    // append data from file asynchronously
150
+                    if([content hasPrefix:FILE_PREFIX])
151
+                    {
152
+                        NSString * orgPath = [content substringFromIndex:[FILE_PREFIX length]];
153
+                        orgPath = [RNFetchBlobFS getPathOfAsset:orgPath];
154
+                        [RNFetchBlobFS readFile:orgPath encoding:@"utf8" resolver:nil rejecter:nil onComplete:^(NSData *content) {
155
+                            NSString * filename = [field valueForKey:@"filename"];
156
+                            [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
157
+                            [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
158
+                            [formData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
159
+                            [formData appendData:content];
160
+                            [formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
161
+                            i++;
162
+                            if(i < count)
163
+                            {
164
+                                getFieldData([form objectAtIndex:i]);
165
+                            }
166
+                            else
167
+                                onComplete(formData);
168
+                        }];
169
+                        return ;
170
+                    }
171
+                    else
172
+                        blobData = [[NSData alloc] initWithBase64EncodedString:content options:0];
173
+                }
174
+                NSString * filename = [field valueForKey:@"filename"];
175
+                [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
176
+                [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
177
+                [formData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
178
+                [formData appendData:blobData];
179
+                [formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
180
+            }
181
+            i++;
182
+            if(i < count)
183
+            {
184
+                getFieldData([form objectAtIndex:i]);
185
+            }
186
+            else
187
+                onComplete(formData);
188
+            
189
+        };
190
+        getFieldData([form objectAtIndex:i]);
191
+    }
192
+}
193
+
194
+
195
+@end

+ 113
- 32
test/test-0.6.2.js Dosyayı Görüntüle

@@ -18,51 +18,132 @@ const { Assert, Comparer, Info, prop } = RNTest
18 18
 const describe = RNTest.config({
19 19
   group : '0.6.2',
20 20
   run : true,
21
-  expand : true,
21
+  expand : false,
22 22
   timeout : 12000,
23 23
 })
24
-const { TEST_SERVER_URL_SSL, FILENAME, DROPBOX_TOKEN, styles } = prop()
24
+const { TEST_SERVER_URL, TEST_SERVER_URL_SSL, DROPBOX_TOKEN, styles } = prop()
25 25
 const  dirs = RNFetchBlob.fs.dirs
26 26
 
27 27
 let prefix = ((Platform.OS === 'android') ? 'file://' : '')
28
+let photo = null
28 29
 
29
-
30
-describe('access assets from camera roll', (report, done) => {
31
-  let photo = null
30
+describe('upload asset from camera roll', (report, done) => {
31
+  let imgName = `image-from-camera-roll-${Platform.OS}.jpg`
32 32
   CameraRoll.getPhotos({first : 10})
33 33
     .then((resp) => {
34
-      photo = resp.edges[0].node.image.uri
35
-      report(<Info key="items">
36
-        <Text>{photo}</Text>
37
-      </Info>)
38
-      return fs.readFile(photo, 'base64')
34
+      let url = resp.edges[0].node.image.uri
35
+      photo = url
36
+      return RNFetchBlob.fetch('POST', 'https://content.dropboxapi.com/2/files/upload', {
37
+        Authorization : `Bearer ${DROPBOX_TOKEN}`,
38
+        'Dropbox-API-Arg': `{\"path\": \"/rn-upload/${imgName}\",\"mode\": \"add\",\"autorename\": false,\"mute\": false}`,
39
+        'Content-Type' : 'application/octet-stream',
40
+      }, RNFetchBlob.wrap(url))
41
+    })
42
+    .then((resp) => {
43
+      resp = resp.json()
44
+      report(
45
+        <Assert key="confirm the file has been uploaded" expect={imgName} actual={resp.name}/>
46
+      )
47
+      done()
48
+    })
49
+})
50
+
51
+describe('Upload multipart data with file from CameraRoll', (report, done) => {
52
+    let filename = 'test-from-storage-img-'+Date.now()+'.png'
53
+    RNFetchBlob.fetch('POST', `${TEST_SERVER_URL}/upload-form`, {
54
+        'Content-Type' : 'multipart/form-data',
55
+      }, [
56
+        { name : 'test-img', filename : filename, data: RNFetchBlob.wrap(photo)},
57
+        { name : 'test-text', filename : 'test-text.txt', data: RNFetchBlob.base64.encode('hello.txt')},
58
+        { name : 'field1', data : 'hello !!'},
59
+        { name : 'field2', data : 'hello2 !!'}
60
+      ])
61
+    .then((resp) => {
62
+      resp = resp.json()
63
+      report(
64
+        <Assert key="check posted form data #1" expect="hello !!" actual={resp.fields.field1}/>,
65
+        <Assert key="check posted form data #2" expect="hello2 !!" actual={resp.fields.field2}/>,
66
+      )
67
+      return RNFetchBlob.fetch('GET', `${TEST_SERVER_URL}/public/${filename}`)
39 68
     })
40
-    .then((data) => {
41
-      report(<Info key="asset image">
69
+    .then((resp) => {
70
+      report(<Info key="uploaded image">
42 71
         <Image
43 72
           style={styles.image}
44
-          source={{uri: `data:image/png;base64, ${data}`}}/>
73
+          source={{ uri : 'data:image/png;base64, '+ resp.base64()}}/>
45 74
       </Info>)
46 75
       done()
47 76
     })
48 77
 })
49 78
 
50
-describe('read asset in app bundle',(report, done) => {
51
-  let target = 'bundle-assets://test-asset2.png'
52
-  fs.readFile(target, 'base64')
53
-  .then((data) => {
54
-    report(<Info key="asset image">
55
-      <Image
56
-        style={styles.image}
57
-        source={{uri: `data:image/png;base64, ${data}`}}/>
58
-    </Info>)
59
-    return fs.readFile('bundle-assets://test-asset1.json', 'utf8')
60
-  })
61
-  .then((resp) => {
62
-    report(
63
-      <Assert key="asset content verify"
64
-        expect="asset#1"
65
-        actual={JSON.parse(resp).secret}/>)
66
-      done()
67
-  })
68
-})
79
+//
80
+// describe('access assets from camera roll', (report, done) => {
81
+//   let photo = null
82
+//   CameraRoll.getPhotos({first : 10})
83
+//     .then((resp) => {
84
+//       photo = resp.edges[0].node.image.uri
85
+//       report(<Info key="items">
86
+//         <Text>{photo}</Text>
87
+//       </Info>)
88
+//       return fs.readFile(photo, 'base64')
89
+//     })
90
+//     .then((data) => {
91
+//       report(<Info key="asset image">
92
+//         <Image
93
+//           style={styles.image}
94
+//           source={{uri: `data:image/png;base64, ${data}`}}/>
95
+//       </Info>)
96
+//       done()
97
+//     })
98
+// })
99
+//
100
+// describe('read asset in app bundle',(report, done) => {
101
+//   let target = fs.asset('test-asset2.png')
102
+//   fs.readFile(target, 'base64')
103
+//   .then((data) => {
104
+//     report(<Info key="asset image">
105
+//       <Image
106
+//         style={styles.image}
107
+//         source={{uri: `data:image/png;base64, ${data}`}}/>
108
+//     </Info>)
109
+//     return fs.readFile(fs.asset('test-asset1.json'), 'utf8')
110
+//   })
111
+//   .then((resp) => {
112
+//     report(
113
+//       <Assert key="asset content verify"
114
+//         expect="asset#1"
115
+//         actual={JSON.parse(resp).secret}/>)
116
+//       done()
117
+//   })
118
+// })
119
+//
120
+// describe('stat assets in app', (report, done) => {
121
+//   fs.stat(fs.asset('test-asset2.png'))
122
+//     .then((data) => {
123
+//       report(<Info key="list of assets">
124
+//         <Text>{JSON.stringify(data)}</Text>
125
+//       </Info>)
126
+//       done()
127
+//     })
128
+// })
129
+//
130
+// describe('copy asset', (report, done) => {
131
+//   let dest = `${dirs.DocumentDir}/test-asset-1-${Date.now()}.json`
132
+//   fs.cp(fs.asset('test-asset1.json'), dest)
133
+//     .then(() => fs.readFile(dest, 'utf8'))
134
+//     .then((data) => {
135
+//       report(<Assert key="asset copied correctly"
136
+//         expect={'asset#1'}
137
+//         actual={JSON.parse(data).secret}/>)
138
+//       return fs.stat(fs.asset('test-asset1.json'))
139
+//     })
140
+//     .then((stat) => {
141
+//       report(<Assert key="file size check"
142
+//         expect={27}
143
+//         actual={Math.floor(stat.size)}/>,
144
+//       <Info key="dest file info">
145
+//         <Text>{JSON.stringify(stat)}</Text>
146
+//       </Info>)
147
+//       done()
148
+//     })
149
+// })

+ 8
- 8
test/test-init.js Dosyayı Görüntüle

@@ -18,8 +18,8 @@ const { Assert, Comparer, Info, describe, prop } = RNTest
18 18
 // test environment variables
19 19
 
20 20
 prop('FILENAME', `${Platform.OS}-0.7.0-${Date.now()}.png`)
21
-prop('TEST_SERVER_URL', 'http://192.168.16.70:8123')
22
-prop('TEST_SERVER_URL_SSL', 'https://192.168.16.70:8124')
21
+prop('TEST_SERVER_URL', 'http://192.168.0.11:8123')
22
+prop('TEST_SERVER_URL_SSL', 'https://192.168.0.11:8124')
23 23
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
24 24
 prop('styles', {
25 25
   image : {
@@ -51,10 +51,10 @@ describe('GET image from server', (report, done) => {
51 51
     })
52 52
 })
53 53
 //
54
-// require('./test-0.1.x-0.4.x')
55
-// require('./test-0.5.1')
56
-// require('./test-0.5.2')
57
-// require('./test-0.6.0')
54
+require('./test-0.1.x-0.4.x')
55
+require('./test-0.5.1')
56
+require('./test-0.5.2')
57
+require('./test-0.6.0')
58 58
 require('./test-0.6.2')
59
-// require('./test-fs')
60
-// require('./test-android')
59
+require('./test-fs')
60
+require('./test-android')