├── index.js ├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── yoloci │ │ └── fileupload │ │ ├── FileUploadPackage.java │ │ ├── FileUploadModule.java │ │ └── BundleJSONConverter.java └── build.gradle ├── package.json ├── LICENSE ├── README.md └── FileUpload.m /index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:1.1.3' 8 | } 9 | } 10 | 11 | apply plugin: 'com.android.library' 12 | 13 | android { 14 | compileSdkVersion 23 15 | buildToolsVersion "23.0.1" 16 | 17 | defaultConfig { 18 | minSdkVersion 16 19 | targetSdkVersion 22 20 | versionCode 1 21 | versionName "1.0" 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | } 27 | 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | dependencies { 33 | compile 'com.facebook.react:react-native:0.15.+' 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fileupload", 3 | "version": "1.1.0", 4 | "description": "A file upload plugin for react-native", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/PhilippKrone/react-native-fileupload.git" 12 | }, 13 | "keywords": [ 14 | "react-component", 15 | "react-native", 16 | "ios", 17 | "file", 18 | "upload" 19 | ], 20 | "author": "Philipp Krone", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/PhilippKrone/react-native-fileupload/issues" 24 | }, 25 | "homepage": "https://github.com/PhilippKrone/react-native-fileupload" 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Liucw & PhilippKrone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /android/src/main/java/com/yoloci/fileupload/FileUploadPackage.java: -------------------------------------------------------------------------------- 1 | package com.yoloci.fileupload; 2 | 3 | 4 | import com.facebook.react.ReactPackage; 5 | import com.facebook.react.bridge.JavaScriptModule; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | public class FileUploadPackage implements ReactPackage { 17 | private FileUploadModule mModule; 18 | 19 | public FileUploadPackage() { 20 | 21 | } 22 | 23 | @Override 24 | public List createNativeModules( 25 | ReactApplicationContext reactContext) { 26 | List modules = new ArrayList<>(); 27 | 28 | mModule = new FileUploadModule(reactContext); 29 | modules.add(mModule); 30 | return modules; 31 | } 32 | 33 | @Override 34 | public List> createJSModules() { 35 | return Collections.emptyList(); 36 | } 37 | 38 | @Override 39 | public List createViewManagers(ReactApplicationContext reactContext) { 40 | return Arrays.asList(); 41 | } 42 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-fileupload [![NPM version](https://img.shields.io/npm/v/react-native-fileupload.svg?style=flat-square)](https://www.npmjs.com/package/react-native-fileupload) 2 | 3 | **Important**: iOS version created by booxood (react-native-file-upload). This repository is the continuation of https://github.com/booxood/react-native-file-upload. 4 | 5 | * Support to upload multiple files at a time 6 | * Support to files and fields 7 | 8 | ## Getting started 9 | 10 | `npm install react-native-fileupload --save` 11 | 12 | ### iOS 13 | 1. In XCode, in the project navigator, right click `your project` ➜ `Add Files to [your project's name]` 14 | 2. Go to `node_modules` ➜ `react-native-fileupload` and add `FileUpload.m` 15 | 3. Run your project (`Cmd+R`) 16 | 17 | ### Android 18 | 19 | *Note: Android support requires React Native 0.12 or later* 20 | 21 | * Edit `android/settings.gradle` to look like this: 22 | 23 | ``` 24 | rootProject.name = 'MyApp' 25 | 26 | include ':app' 27 | 28 | //Add the following two lines: 29 | include ':react-native-fileupload' 30 | project(':react-native-fileupload').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fileupload/android') 31 | ``` 32 | 33 | * Edit `android/app/build.gradle` (note: **app** folder) to look like this: 34 | 35 | ``` 36 | apply plugin: 'com.android.application' 37 | 38 | android { 39 | ... 40 | } 41 | 42 | dependencies { 43 | compile fileTree(dir: 'libs', include: ['*.jar']) 44 | compile 'com.android.support:appcompat-v7:23.0.0' 45 | compile 'com.facebook.react:react-native:0.12.+' 46 | 47 | // Add this line: 48 | compile project(':react-native-fileupload') 49 | } 50 | ``` 51 | 52 | * Edit your `MainActivity.java` (deep in `android/app/src/main/java/...`) to look like this: 53 | 54 | ``` 55 | package com.myapp; 56 | 57 | // Add this line: 58 | import com.yoloci.fileupload.FileUploadPackage; 59 | 60 | import android.app.Activity; 61 | .... 62 | 63 | public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler { 64 | 65 | private ReactInstanceManager mReactInstanceManager; 66 | private ReactRootView mReactRootView; 67 | 68 | @Override 69 | protected void onCreate(Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | mReactRootView = new ReactRootView(this); 72 | 73 | mReactInstanceManager = ReactInstanceManager.builder() 74 | .setApplication(getApplication()) 75 | .setBundleAssetName("index.android.bundle") 76 | .setJSMainModuleName("index.android") 77 | .addPackage(new MainReactPackage()) 78 | 79 | // and this line: 80 | .addPackage(new FileUploadPackage()) 81 | 82 | .setUseDeveloperSupport(BuildConfig.DEBUG) 83 | .setInitialLifecycleState(LifecycleState.RESUMED) 84 | .build(); 85 | 86 | mReactRootView.startReactApplication(mReactInstanceManager, "MyApp", null); 87 | 88 | setContentView(mReactRootView); 89 | } 90 | ... 91 | } 92 | ``` 93 | 94 | ## Usage 95 | 96 | All you need is to export module `var FileUpload = require('NativeModules').FileUpload;` and direct invoke `FileUpload.upload`. 97 | 98 | ```javascript 99 | 'use strict'; 100 | 101 | var React = require('react-native'); 102 | var FileUpload = require('NativeModules').FileUpload; 103 | 104 | var { 105 | AppRegistry, 106 | StyleSheet, 107 | Text, 108 | View, 109 | } = React; 110 | 111 | var FileUploadDemo = React.createClass({ 112 | componentDidMount: function() { 113 | var obj = { 114 | uploadUrl: 'http://127.0.0.1:3000', 115 | method: 'POST', // default 'POST',support 'POST' and 'PUT' 116 | headers: { 117 | 'Accept': 'application/json', 118 | }, 119 | fields: { 120 | 'hello': 'world', 121 | }, 122 | files: [ 123 | { 124 | name: 'one', // optional, if none then `filename` is used instead 125 | filename: 'one.w4a', // require, file name 126 | filepath: '/xxx/one.w4a', // require, file absoluete path 127 | filetype: 'audio/x-m4a', // options, if none, will get mimetype from `filepath` extension 128 | }, 129 | ] 130 | }; 131 | FileUpload.upload(obj, function(err, result) { 132 | console.log('upload:', err, result); 133 | }) 134 | }, 135 | render: function() { 136 | return ( 137 | 138 | 139 | Welcome to React Native! 140 | 141 | 142 | ); 143 | } 144 | }); 145 | 146 | var styles = StyleSheet.create({ 147 | container: { 148 | flex: 1, 149 | justifyContent: 'center', 150 | alignItems: 'center', 151 | backgroundColor: '#F5FCFF', 152 | }, 153 | welcome: { 154 | fontSize: 20, 155 | textAlign: 'center', 156 | margin: 10, 157 | }, 158 | }); 159 | 160 | AppRegistry.registerComponent('FileUploadDemo', () => FileUploadDemo); 161 | ``` 162 | 163 | ## License 164 | 165 | MIT 166 | -------------------------------------------------------------------------------- /FileUpload.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #import "RCTBridgeModule.h" 7 | #import "RCTLog.h" 8 | 9 | @interface FileUpload : NSObject 10 | @end 11 | 12 | @implementation FileUpload 13 | 14 | RCT_EXPORT_MODULE(); 15 | 16 | RCT_EXPORT_METHOD(upload:(NSDictionary *)obj callback:(RCTResponseSenderBlock)callback) 17 | { 18 | NSString *uploadUrl = obj[@"uploadUrl"]; 19 | NSDictionary *headers = obj[@"headers"]; 20 | NSDictionary *fields = obj[@"fields"]; 21 | NSArray *files = obj[@"files"]; 22 | NSString *method = obj[@"method"]; 23 | 24 | if ([method isEqualToString:@"POST"] || [method isEqualToString:@"PUT"]) { 25 | } else { 26 | method = @"POST"; 27 | } 28 | 29 | NSURL *url = [NSURL URLWithString:uploadUrl]; 30 | NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url]; 31 | [req setHTTPMethod:method]; 32 | 33 | // set headers 34 | NSString *formBoundaryString = [self generateBoundaryString]; 35 | NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", formBoundaryString]; 36 | [req setValue:contentType forHTTPHeaderField:@"Content-Type"]; 37 | for (NSString *key in headers) { 38 | id val = [headers objectForKey:key]; 39 | if ([val respondsToSelector:@selector(stringValue)]) { 40 | val = [val stringValue]; 41 | } 42 | if (![val isKindOfClass:[NSString class]]) { 43 | continue; 44 | } 45 | [req setValue:val forHTTPHeaderField:key]; 46 | } 47 | 48 | 49 | NSData *formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding]; 50 | NSMutableData* reqBody = [NSMutableData data]; 51 | 52 | // add fields 53 | for (NSString *key in fields) { 54 | id val = [fields objectForKey:key]; 55 | if ([val respondsToSelector:@selector(stringValue)]) { 56 | val = [val stringValue]; 57 | } 58 | if (![val isKindOfClass:[NSString class]]) { 59 | continue; 60 | } 61 | 62 | [reqBody appendData:formBoundaryData]; 63 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; 64 | [reqBody appendData:[val dataUsingEncoding:NSUTF8StringEncoding]]; 65 | [reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 66 | } 67 | 68 | // add files 69 | for (NSDictionary *file in files) { 70 | NSString *name = file[@"name"]; 71 | NSString *filename = file[@"filename"]; 72 | NSString *filepath = file[@"filepath"]; 73 | NSString *filetype = file[@"filetype"]; 74 | 75 | NSData *fileData = nil; 76 | 77 | NSLog(@"filepath: %@", filepath); 78 | if ([filepath hasPrefix:@"assets-library:"]) { 79 | NSURL *assetUrl = [[NSURL alloc] initWithString:filepath]; 80 | ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 81 | 82 | __block BOOL isFinished = NO; 83 | __block NSData * tempData = nil; 84 | [library assetForURL:assetUrl resultBlock:^(ALAsset *asset) { 85 | ALAssetRepresentation *rep = [asset defaultRepresentation]; 86 | 87 | CGImageRef fullScreenImageRef = [rep fullScreenImage]; 88 | UIImage *image = [UIImage imageWithCGImage:fullScreenImageRef]; 89 | tempData = UIImagePNGRepresentation(image); 90 | isFinished = YES; 91 | } failureBlock:^(NSError *error) { 92 | NSLog(@"ALAssetsLibrary assetForURL error:%@", error); 93 | isFinished = YES; 94 | }]; 95 | while (!isFinished) { 96 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]]; 97 | } 98 | fileData = tempData; 99 | } else if ([filepath hasPrefix:@"data:"] || [filepath hasPrefix:@"file:"]) { 100 | NSURL *fileUrl = [[NSURL alloc] initWithString:filepath]; 101 | fileData = [NSData dataWithContentsOfURL: fileUrl]; 102 | } else { 103 | fileData = [NSData dataWithContentsOfFile:filepath]; 104 | } 105 | 106 | [reqBody appendData:formBoundaryData]; 107 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name.length ? name : filename, filename] dataUsingEncoding:NSUTF8StringEncoding]]; 108 | 109 | if (filetype) { 110 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", filetype] dataUsingEncoding:NSUTF8StringEncoding]]; 111 | } else { 112 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", [self mimeTypeForPath:filename]] dataUsingEncoding:NSUTF8StringEncoding]]; 113 | } 114 | 115 | [reqBody appendData:[[NSString stringWithFormat:@"Content-Length: %ld\r\n\r\n", (long)[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]]; 116 | [reqBody appendData:fileData]; 117 | [reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 118 | } 119 | 120 | // add end boundary 121 | NSData* end = [[NSString stringWithFormat:@"--%@--\r\n", formBoundaryString] dataUsingEncoding:NSUTF8StringEncoding]; 122 | [reqBody appendData:end]; 123 | 124 | // send request 125 | [req setHTTPBody:reqBody]; 126 | NSHTTPURLResponse *response = nil; 127 | NSData *returnData = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:nil]; 128 | NSInteger statusCode = [response statusCode]; 129 | NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding]; 130 | 131 | NSDictionary *res=[[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInteger:statusCode],@"status",returnString,@"data",nil]; 132 | 133 | callback(@[[NSNull null], res]); 134 | } 135 | 136 | - (NSString *)generateBoundaryString 137 | { 138 | NSString *uuid = [[NSUUID UUID] UUIDString]; 139 | return [NSString stringWithFormat:@"----%@", uuid]; 140 | } 141 | 142 | - (NSString *)mimeTypeForPath:(NSString *)filepath 143 | { 144 | NSString *fileExtension = [filepath pathExtension]; 145 | NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL); 146 | NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); 147 | 148 | if (contentType) { 149 | return contentType; 150 | } 151 | return @"application/octet-stream"; 152 | } 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /android/src/main/java/com/yoloci/fileupload/FileUploadModule.java: -------------------------------------------------------------------------------- 1 | package com.yoloci.fileupload; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.facebook.react.bridge.Arguments; 6 | import com.facebook.react.bridge.ReadableArray; 7 | import com.facebook.react.bridge.ReadableMap; 8 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 11 | import com.facebook.react.bridge.ReactMethod; 12 | import com.facebook.react.bridge.Callback; 13 | 14 | import java.io.DataInputStream; 15 | import java.net.HttpURLConnection; 16 | import java.net.URL; 17 | import java.io.DataOutputStream; 18 | import java.io.BufferedReader; 19 | import java.io.InputStreamReader; 20 | 21 | import com.facebook.react.bridge.WritableMap; 22 | import java.io.FileInputStream; 23 | 24 | import org.json.JSONObject; 25 | 26 | public class FileUploadModule extends ReactContextBaseJavaModule { 27 | 28 | @Override 29 | public String getName() { 30 | return "FileUpload"; 31 | } 32 | 33 | public FileUploadModule(ReactApplicationContext reactContext) { 34 | super(reactContext); 35 | } 36 | 37 | @ReactMethod 38 | public void upload(final ReadableMap options, final Callback callback) { 39 | String lineEnd = "\r\n"; 40 | String twoHyphens = "--"; 41 | String boundary = "*****"; 42 | 43 | String uploadUrl = options.getString("uploadUrl"); 44 | String method; 45 | if (options.hasKey("method")) { 46 | method = options.getString("method"); 47 | } else { 48 | method = "POST"; 49 | } 50 | 51 | ReadableMap headers = options.getMap("headers"); 52 | ReadableArray files = options.getArray("files"); 53 | ReadableMap fields = options.getMap("fields"); 54 | 55 | 56 | 57 | HttpURLConnection connection = null; 58 | DataOutputStream outputStream = null; 59 | DataInputStream inputStream = null; 60 | URL connectURL = null; 61 | FileInputStream fileInputStream = null; 62 | 63 | int bytesRead, bytesAvailable, bufferSize; 64 | byte[] buffer; 65 | int maxBufferSize = 1*1024*1024; 66 | 67 | try { 68 | 69 | connectURL = new URL(uploadUrl); 70 | 71 | 72 | connection = (HttpURLConnection) connectURL.openConnection(); 73 | 74 | // Allow Inputs & Outputs. 75 | connection.setDoInput(true); 76 | connection.setDoOutput(true); 77 | connection.setUseCaches(false); 78 | 79 | connection.setRequestMethod(method); 80 | 81 | // set headers 82 | ReadableMapKeySetIterator iterator = headers.keySetIterator(); 83 | while (iterator.hasNextKey()) { 84 | String key = iterator.nextKey(); 85 | connection.setRequestProperty(key, headers.getString(key)); 86 | } 87 | 88 | 89 | 90 | connection.setRequestProperty("Connection", "Keep-Alive"); 91 | connection.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary); 92 | 93 | outputStream = new DataOutputStream( connection.getOutputStream() ); 94 | 95 | // set fields 96 | ReadableMapKeySetIterator fieldIterator = fields.keySetIterator(); 97 | while (fieldIterator.hasNextKey()) { 98 | outputStream.writeBytes(twoHyphens + boundary + lineEnd); 99 | 100 | String key = fieldIterator.nextKey(); 101 | outputStream.writeBytes("Content-Disposition: form-data; name=\"" + key + "\"" + lineEnd + lineEnd); 102 | outputStream.writeBytes(fields.getString(key)); 103 | outputStream.writeBytes(lineEnd); 104 | } 105 | 106 | 107 | for (int i = 0; i < files.size(); i++) { 108 | 109 | ReadableMap file = files.getMap(i); 110 | String filename = file.getString("filename"); 111 | String filepath = file.getString("filepath"); 112 | filepath = filepath.replace("file://", ""); 113 | fileInputStream = new FileInputStream(filepath); 114 | 115 | outputStream.writeBytes(twoHyphens + boundary + lineEnd); 116 | outputStream.writeBytes("Content-Disposition: form-data; name=\"image\";filename=\"" + filename + "\"" + lineEnd); 117 | outputStream.writeBytes(lineEnd); 118 | 119 | bytesAvailable = fileInputStream.available(); 120 | bufferSize = Math.min(bytesAvailable, maxBufferSize); 121 | buffer = new byte[bufferSize]; 122 | 123 | // Read file 124 | bytesRead = fileInputStream.read(buffer, 0, bufferSize); 125 | 126 | while (bytesRead > 0) { 127 | outputStream.write(buffer, 0, bufferSize); 128 | bytesAvailable = fileInputStream.available(); 129 | bufferSize = Math.min(bytesAvailable, maxBufferSize); 130 | bytesRead = fileInputStream.read(buffer, 0, bufferSize); 131 | } 132 | 133 | outputStream.writeBytes(lineEnd); 134 | } 135 | 136 | outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); 137 | 138 | // Responses from the server (code and message) 139 | 140 | int serverResponseCode = connection.getResponseCode(); 141 | String serverResponseMessage = connection.getResponseMessage(); 142 | if (serverResponseCode != 200) { 143 | fileInputStream.close(); 144 | outputStream.flush(); 145 | outputStream.close(); 146 | callback.invoke("Error happened: " + serverResponseMessage, null); 147 | } else { 148 | BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); 149 | StringBuilder sb = new StringBuilder(); 150 | String line; 151 | while ((line = br.readLine()) != null) { 152 | sb.append(line); 153 | } 154 | br.close(); 155 | String data = sb.toString(); 156 | JSONObject mainObject = new JSONObject(); 157 | mainObject.put("data", data); 158 | mainObject.put("status", serverResponseCode); 159 | 160 | BundleJSONConverter bjc = new BundleJSONConverter(); 161 | Bundle bundle = bjc.convertToBundle(mainObject); 162 | WritableMap map = Arguments.fromBundle(bundle); 163 | 164 | fileInputStream.close(); 165 | outputStream.flush(); 166 | outputStream.close(); 167 | callback.invoke(null, map); 168 | } 169 | 170 | 171 | 172 | } catch(Exception ex) { 173 | callback.invoke("Error happened: " + ex.getMessage(), null); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /android/src/main/java/com/yoloci/fileupload/BundleJSONConverter.java: -------------------------------------------------------------------------------- 1 | package com.yoloci.fileupload; 2 | 3 | import android.os.Bundle; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. 17 | * 18 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 19 | * copy, modify, and distribute this software in source code or binary form for use 20 | * in connection with the web services and APIs provided by Facebook. 21 | * 22 | * As with any software that integrates with the Facebook platform, your use of 23 | * this software is subject to the Facebook Developer Principles and Policies 24 | * [http://developers.facebook.com/policy/]. This copyright notice shall be 25 | * included in all copies or substantial portions of the software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 29 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 30 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 31 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 32 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | */ 34 | public class BundleJSONConverter { 35 | private static final Map, Setter> SETTERS = new HashMap, Setter>(); 36 | 37 | static { 38 | SETTERS.put(Boolean.class, new Setter() { 39 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException { 40 | bundle.putBoolean(key, (Boolean) value); 41 | } 42 | 43 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException { 44 | json.put(key, value); 45 | } 46 | }); 47 | SETTERS.put(Integer.class, new Setter() { 48 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException { 49 | bundle.putInt(key, (Integer) value); 50 | } 51 | 52 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException { 53 | json.put(key, value); 54 | } 55 | }); 56 | SETTERS.put(Long.class, new Setter() { 57 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException { 58 | bundle.putLong(key, (Long) value); 59 | } 60 | 61 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException { 62 | json.put(key, value); 63 | } 64 | }); 65 | SETTERS.put(Double.class, new Setter() { 66 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException { 67 | bundle.putDouble(key, (Double) value); 68 | } 69 | 70 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException { 71 | json.put(key, value); 72 | } 73 | }); 74 | SETTERS.put(String.class, new Setter() { 75 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException { 76 | bundle.putString(key, (String) value); 77 | } 78 | 79 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException { 80 | json.put(key, value); 81 | } 82 | }); 83 | SETTERS.put(String[].class, new Setter() { 84 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException { 85 | throw new IllegalArgumentException("Unexpected type from JSON"); 86 | } 87 | 88 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException { 89 | JSONArray jsonArray = new JSONArray(); 90 | for (String stringValue : (String[])value) { 91 | jsonArray.put(stringValue); 92 | } 93 | json.put(key, jsonArray); 94 | } 95 | }); 96 | 97 | SETTERS.put(JSONArray.class, new Setter() { 98 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException { 99 | JSONArray jsonArray = (JSONArray)value; 100 | ArrayList stringArrayList = new ArrayList(); 101 | // Empty list, can't even figure out the type, assume an ArrayList 102 | if (jsonArray.length() == 0) { 103 | bundle.putStringArrayList(key, stringArrayList); 104 | return; 105 | } 106 | 107 | // Only strings are supported for now 108 | for (int i = 0; i < jsonArray.length(); i++) { 109 | Object current = jsonArray.get(i); 110 | if (current instanceof String) { 111 | stringArrayList.add((String)current); 112 | } else { 113 | throw new IllegalArgumentException("Unexpected type in an array: " + current.getClass()); 114 | } 115 | } 116 | bundle.putStringArrayList(key, stringArrayList); 117 | } 118 | 119 | @Override 120 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException { 121 | throw new IllegalArgumentException("JSONArray's are not supported in bundles."); 122 | } 123 | }); 124 | } 125 | 126 | public interface Setter { 127 | public void setOnBundle(Bundle bundle, String key, Object value) throws JSONException; 128 | public void setOnJSON(JSONObject json, String key, Object value) throws JSONException; 129 | } 130 | 131 | public static JSONObject convertToJSON(Bundle bundle) throws JSONException { 132 | JSONObject json = new JSONObject(); 133 | 134 | for(String key : bundle.keySet()) { 135 | Object value = bundle.get(key); 136 | if (value == null) { 137 | // Null is not supported. 138 | continue; 139 | } 140 | 141 | // Special case List as getClass would not work, since List is an interface 142 | if (value instanceof List) { 143 | JSONArray jsonArray = new JSONArray(); 144 | @SuppressWarnings("unchecked") 145 | List listValue = (List)value; 146 | for (String stringValue : listValue) { 147 | jsonArray.put(stringValue); 148 | } 149 | json.put(key, jsonArray); 150 | continue; 151 | } 152 | 153 | // Special case Bundle as it's one way, on the return it will be JSONObject 154 | if (value instanceof Bundle) { 155 | json.put(key, convertToJSON((Bundle)value)); 156 | continue; 157 | } 158 | 159 | Setter setter = SETTERS.get(value.getClass()); 160 | if (setter == null) { 161 | throw new IllegalArgumentException("Unsupported type: " + value.getClass()); 162 | } 163 | setter.setOnJSON(json, key, value); 164 | } 165 | 166 | return json; 167 | } 168 | 169 | public static Bundle convertToBundle(JSONObject jsonObject) throws JSONException { 170 | Bundle bundle = new Bundle(); 171 | @SuppressWarnings("unchecked") 172 | Iterator jsonIterator = jsonObject.keys(); 173 | while (jsonIterator.hasNext()) { 174 | String key = jsonIterator.next(); 175 | Object value = jsonObject.get(key); 176 | if (value == null || value == JSONObject.NULL) { 177 | // Null is not supported. 178 | continue; 179 | } 180 | 181 | // Special case JSONObject as it's one way, on the return it would be Bundle. 182 | if (value instanceof JSONObject) { 183 | bundle.putBundle(key, convertToBundle((JSONObject)value)); 184 | continue; 185 | } 186 | 187 | Setter setter = SETTERS.get(value.getClass()); 188 | if (setter == null) { 189 | throw new IllegalArgumentException("Unsupported type: " + value.getClass()); 190 | } 191 | setter.setOnBundle(bundle, key, value); 192 | } 193 | 194 | return bundle; 195 | } 196 | } 197 | --------------------------------------------------------------------------------