├── 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 [](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 |
--------------------------------------------------------------------------------