diff --git a/README.md b/README.md index d64c6725..f7cc42e3 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # react-native-background-upload [![npm version](https://badge.fury.io/js/react-native-background-upload.svg)](https://badge.fury.io/js/react-native-background-upload) ![GitHub Actions status](https://github.com/Vydia/react-native-background-upload/workflows/Test%20iOS%20Example%20App/badge.svg) ![GitHub Actions status](https://github.com/Vydia/react-native-background-upload/workflows/Test%20Android%20Example%20App/badge.svg) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) -The only React Native http post file uploader with android and iOS background support. If you are uploading large files like videos, use this so your users can background your app during a long upload. +The only React Native http post file uploader with android and iOS background support. If you are uploading large files like videos, use this so your users can background your app during a long upload. -NOTE: Use major version 4 with RN 47.0 and greater. If you have RN less than 47, use 3.0. To view all available versions: +NOTE: Use major version 4 with RN 47.0 and greater. If you have RN less than 47, use 3.0. To view all available versions: `npm show react-native-background-upload versions` - # Installation ## 1. Install package @@ -29,6 +28,7 @@ Note: if you are installing on React Native < 0.47, use `react-native-background ##### Android ###### ProGuard + Add this to your ProGuard configuration: `-keep class net.gotev.uploadservice.** { *; }` @@ -46,40 +46,41 @@ Add this to your ProGuard configuration: 3. Add `VydiaRNFileUploader.a` to `Build Phases -> Link Binary With Libraries` #### Android + 1. Add the following lines to `android/settings.gradle`: - ```gradle - include ':react-native-background-upload' - project(':react-native-background-upload').projectDir = new File(settingsDir, '../node_modules/react-native-background-upload/android') - ``` + ```gradle + include ':react-native-background-upload' + project(':react-native-background-upload').projectDir = new File(settingsDir, '../node_modules/react-native-background-upload/android') + ``` + 2. Add the compile and resolutionStrategy line to the dependencies in `android/app/build.gradle`: - ```gradle - configurations.all { resolutionStrategy.force 'com.squareup.okhttp3:okhttp:3.4.1' } // required by react-native-background-upload until React Native supports okhttp >= okhttp 3.5 + ```gradle + configurations.all { resolutionStrategy.force 'com.squareup.okhttp3:okhttp:3.4.1' } // required by react-native-background-upload until React Native supports okhttp >= okhttp 3.5 - dependencies { - compile project(':react-native-background-upload') - } - ``` + dependencies { + compile project(':react-native-background-upload') + } + ``` +3) Add the import and link the package in `MainApplication.java`: -3. Add the import and link the package in `MainApplication.java`: + ```java + import com.vydia.RNUploader.UploaderReactPackage; <-- add this import - ```java - import com.vydia.RNUploader.UploaderReactPackage; <-- add this import - - public class MainApplication extends Application implements ReactApplication { - @Override - protected List getPackages() { - return Arrays.asList( - new MainReactPackage(), - new UploaderReactPackage() // <-- add this line - ); - } - } - ``` + public class MainApplication extends Application implements ReactApplication { + @Override + protected List getPackages() { + return Arrays.asList( + new MainReactPackage(), + new UploaderReactPackage() // <-- add this line + ); + } + } + ``` -4. Ensure Android SDK versions. Open your app's `android/app/build.gradle` file. Ensure `compileSdkVersion` and `targetSdkVersion` are 25. Otherwise you'll get compilation errors. +4) Ensure Android SDK versions. Open your app's `android/app/build.gradle` file. Ensure `compileSdkVersion` and `targetSdkVersion` are 25. Otherwise you'll get compilation errors. ## 3. Expo @@ -88,7 +89,7 @@ To use this library with [Expo](https://expo.io) one must first detach (eject) t # Usage ```js -import Upload from 'react-native-background-upload' +import Upload from 'react-native-background-upload'; const options = { url: 'https://myservice.com/path/to/post', @@ -98,38 +99,40 @@ const options = { maxRetries: 2, // set retry count (Android only). Default 2 headers: { 'content-type': 'application/octet-stream', // Customize content-type - 'my-custom-header': 's3headervalueorwhateveryouneed' + 'my-custom-header': 's3headervalueorwhateveryouneed', }, // Below are options only supported on Android notification: { - enabled: true + enabled: true, }, - useUtf8Charset: true -} - -Upload.startUpload(options).then((uploadId) => { - console.log('Upload started') - Upload.addListener('progress', uploadId, (data) => { - console.log(`Progress: ${data.progress}%`) + useUtf8Charset: true, +}; + +Upload.startUpload(options) + .then(uploadId => { + console.log('Upload started'); + Upload.addListener('progress', uploadId, data => { + console.log(`Progress: ${data.progress}%`); + }); + Upload.addListener('error', uploadId, data => { + console.log(`Error: ${data.error}%`); + }); + Upload.addListener('cancelled', uploadId, data => { + console.log(`Cancelled!`); + }); + Upload.addListener('completed', uploadId, data => { + // data includes responseCode: number and responseBody: Object + console.log('Completed!'); + }); }) - Upload.addListener('error', uploadId, (data) => { - console.log(`Error: ${data.error}%`) - }) - Upload.addListener('cancelled', uploadId, (data) => { - console.log(`Cancelled!`) - }) - Upload.addListener('completed', uploadId, (data) => { - // data includes responseCode: number and responseBody: Object - console.log('Completed!') - }) -}).catch((err) => { - console.log('Upload error!', err) -}) + .catch(err => { + console.log('Upload error!', err); + }); ``` ## Multipart Uploads -Just set the `type` option to `multipart` and set the `field` option. Example: +Just set the `type` option to `multipart` and set the `field` option. Example: ``` const options = { @@ -143,6 +146,47 @@ const options = { Note the `field` property is required for multipart uploads. +## Android only, multiple files upload in one shot via Multipart + +Send all the files in `files` param. In this case `path`, `field` & `type` will be ignored. Example: + +``` +const options = { + url: 'https://myservice.com/path/to/post', + files: [ + { + path: 'file://path/to/file%20on%20device.png', + field: 'uploaded_media' + } + ], + method: 'POST' +} +``` + +Note that `type` will always be multipart when `files` is passed + +## Android only, multiple parameters in one shot via Multipart + +Send parameters as list of string. + +``` +const options = { + url: 'https://myservice.com/path/to/post', + files: [ + { + path: 'file://path/to/file%20on%20device.png', + field: 'uploaded_media' + } + ], + paramaters: { + urls: ["https://example.com", "http://example.com"] + } + method: 'POST' +} +``` + +However, if you pass the array with single value array like ["example.com"], it will be count as string in your backend. It is using request.addArrayParameter("key", List) in native code. + # API ## Top Level Functions @@ -153,58 +197,59 @@ All top-level methods are available as named exports or methods on the default e The primary method you will use, this starts the upload process. -Returns a promise with the string ID of the upload. Will reject if there is a connection problem, the file doesn't exist, or there is some other problem. +Returns a promise with the string ID of the upload. Will reject if there is a connection problem, the file doesn't exist, or there is some other problem. `options` is an object with following values: -*Note: You must provide valid URIs. react-native-background-upload does not escape the values you provide.* - -|Name|Type|Required|Default|Description|Example| -|---|---|---|---|---|---| -|`url`|string|Required||URL to upload to|`https://myservice.com/path/to/post`| -|`path`|string|Required||File path on device|`file://something/coming/from%20the%20device.png`| -|`type`|'raw' or 'multipart'|Optional|`raw`|Primary upload type.|| -|`method`|string|Optional|`POST`|HTTP method|| -|`customUploadId`|string|Optional||`startUpload` returns a Promise that includes the upload ID, which can be used for future status checks. By default, the upload ID is automatically generated. This parameter allows a custom ID to use instead of the default.|| -|`headers`|object|Optional||HTTP headers|`{ 'Accept': 'application/json' }`| -|`field`|string|Required if `type: 'multipart'`||The form field name for the file. Only used when `type: 'multipart`|`uploaded-file`| -|`parameters`|object|Optional||Additional form fields to include in the HTTP request. Only used when `type: 'multipart`|| -|`notification`|Notification object (see below)|Optional||Android only. |`{ enabled: true, onProgressTitle: "Uploading...", autoClear: true }`| -|`useUtf8Charset`|boolean|Optional||Android only. Set to true to use `utf-8` as charset. || -|`appGroup`|string|Optional|iOS only. App group ID needed for share extensions to be able to properly call the library. See: https://developer.apple.com/documentation/foundation/nsfilemanager/1412643-containerurlforsecurityapplicati +_Note: You must provide valid URIs. react-native-background-upload does not escape the values you provide._ + +| Name | Type | Required | Default | Description | Example | +| ---------------- | ------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| `files` | array | Optional | | Android only, to upload multiple files in one request | `[{path: 'file://path/to/file%20on%20device.png', field: 'uploaded_media'}, {path: '', field: ''}]` | +| `url` | string | Required | | URL to upload to | `https://myservice.com/path/to/post` | +| `path` | string | Required | | File path on device | `file://something/coming/from%20the%20device.png` | +| `type` | 'raw' or 'multipart' | Optional | `raw` | Primary upload type. | | +| `method` | string | Optional | `POST` | HTTP method | | +| `customUploadId` | string | Optional | | `startUpload` returns a Promise that includes the upload ID, which can be used for future status checks. By default, the upload ID is automatically generated. This parameter allows a custom ID to use instead of the default. | | +| `headers` | object | Optional | | HTTP headers | `{ 'Accept': 'application/json' }` | +| `field` | string | Required if `type: 'multipart'` | | The form field name for the file. Only used when `type: 'multipart` | `uploaded-file` | +| `parameters` | object | Optional | | Additional form fields to include in the HTTP request. Only used when `type: 'multipart` | | +| `notification` | Notification object (see below) | Optional | | Android only. | `{ enabled: true, onProgressTitle: "Uploading...", autoClear: true }` | +| `useUtf8Charset` | boolean | Optional | | Android only. Set to true to use `utf-8` as charset. | | +| `appGroup` | string | Optional | iOS only. App group ID needed for share extensions to be able to properly call the library. See: https://developer.apple.com/documentation/foundation/nsfilemanager/1412643-containerurlforsecurityapplicati | ### Notification Object (Android Only) -|Name|Type|Required|Description|Example| -|---|---|---|---|---| -|`enabled`|boolean|Optional|Enable or diasable notifications. Works only on Android version < 8.0 Oreo. On Android versions >= 8.0 Oreo is required by Google's policy to display a notification when a background service run|`{ enabled: true }`| -|`autoClear`|boolean|Optional|Autoclear notification on complete|`{ autoclear: true }`| -|`notificationChannel`|string|Optional|Sets android notificaion channel|`{ notificationChannel: "My-Upload-Service" }`| -|`enableRingTone`|boolean|Optional|Sets whether or not to enable the notification sound when the upload gets completed with success or error|`{ enableRingTone: true }`| -|`onProgressTitle`|string|Optional|Sets notification progress title|`{ onProgressTitle: "Uploading" }`| -|`onProgressMessage`|string|Optional|Sets notification progress message|`{ onProgressMessage: "Uploading new video" }`| -|`onCompleteTitle`|string|Optional|Sets notification complete title|`{ onCompleteTitle: "Upload finished" }`| -|`onCompleteMessage`|string|Optional|Sets notification complete message|`{ onCompleteMessage: "Your video has been uploaded" }`| -|`onErrorTitle`|string|Optional|Sets notification error title|`{ onErrorTitle: "Upload error" }`| -|`onErrorMessage`|string|Optional|Sets notification error message|`{ onErrorMessage: "An error occured while uploading a video" }`| -|`onCancelledTitle`|string|Optional|Sets notification cancelled title|`{ onCancelledTitle: "Upload cancelled" }`| -|`onCancelledMessage`|string|Optional|Sets notification cancelled message|`{ onCancelledMessage: "Video upload was cancelled" }`| +| Name | Type | Required | Description | Example | +| --------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| `enabled` | boolean | Optional | Enable or diasable notifications. Works only on Android version < 8.0 Oreo. On Android versions >= 8.0 Oreo is required by Google's policy to display a notification when a background service run | `{ enabled: true }` | +| `autoClear` | boolean | Optional | Autoclear notification on complete | `{ autoclear: true }` | +| `notificationChannel` | string | Optional | Sets android notificaion channel | `{ notificationChannel: "My-Upload-Service" }` | +| `enableRingTone` | boolean | Optional | Sets whether or not to enable the notification sound when the upload gets completed with success or error | `{ enableRingTone: true }` | +| `onProgressTitle` | string | Optional | Sets notification progress title | `{ onProgressTitle: "Uploading" }` | +| `onProgressMessage` | string | Optional | Sets notification progress message | `{ onProgressMessage: "Uploading new video" }` | +| `onCompleteTitle` | string | Optional | Sets notification complete title | `{ onCompleteTitle: "Upload finished" }` | +| `onCompleteMessage` | string | Optional | Sets notification complete message | `{ onCompleteMessage: "Your video has been uploaded" }` | +| `onErrorTitle` | string | Optional | Sets notification error title | `{ onErrorTitle: "Upload error" }` | +| `onErrorMessage` | string | Optional | Sets notification error message | `{ onErrorMessage: "An error occured while uploading a video" }` | +| `onCancelledTitle` | string | Optional | Sets notification cancelled title | `{ onCancelledTitle: "Upload cancelled" }` | +| `onCancelledMessage` | string | Optional | Sets notification cancelled message | `{ onCancelledMessage: "Video upload was cancelled" }` | ### getFileInfo(path) -Returns some useful information about the file in question. Useful if you want to set a MIME type header. +Returns some useful information about the file in question. Useful if you want to set a MIME type header. `path` is a string, such as `file://path.to.the.file.png` Returns a Promise that resolves to an object containing: -|Name|Type|Required|Description|Example| -|---|---|---|---|---| -|`name`|string|Required|The file name within its directory.|`image2.png`| -|`exists`|boolean|Required|Is there a file matching this path?|| -|`size`|number|If `exists`|File size, in bytes|| -|`extension`|string|If `exists`|File extension|`mov`| -|`mimeType`|string|If `exists`|The MIME type for the file.|`video/mp4`| +| Name | Type | Required | Description | Example | +| ----------- | ------- | ----------- | ----------------------------------- | ------------ | +| `name` | string | Required | The file name within its directory. | `image2.png` | +| `exists` | boolean | Required | Is there a file matching this path? | | +| `size` | number | If `exists` | File size, in bytes | | +| `extension` | string | If `exists` | File extension | `mov` | +| `mimeType` | string | If `exists` | The MIME type for the file. | `video/mp4` | ### cancelUpload(uploadId) @@ -220,7 +265,7 @@ Adds an event listener, possibly confined to a single upload. `eventType` Event to listen for. Values: 'progress' | 'error' | 'completed' | 'cancelled' -`uploadId` The upload ID from `startUpload` to filter events for. If null, this will include all uploads. +`uploadId` The upload ID from `startUpload` to filter events for. If null, this will include all uploads. `listener` Function to call when the event occurs. @@ -232,41 +277,42 @@ Returns an [EventSubscription](https://github.com/facebook/react-native/blob/mas Event Data -|Name|Type|Required|Description| -|---|---|---|---| -|`id`|string|Required|The ID of the upload.| -|`progress`|0-100|Required|Percentage completed.| +| Name | Type | Required | Description | +| ---------- | ------ | -------- | --------------------- | +| `id` | string | Required | The ID of the upload. | +| `progress` | 0-100 | Required | Percentage completed. | ### error Event Data -|Name|Type|Required|Description| -|---|---|---|---| -|`id`|string|Required|The ID of the upload.| -|`error`|string|Required|Error message.| +| Name | Type | Required | Description | +| ------- | ------ | -------- | --------------------- | +| `id` | string | Required | The ID of the upload. | +| `error` | string | Required | Error message. | ### completed Event Data -|Name|Type|Required|Description| -|---|---|---|---| -|`id`|string|Required|The ID of the upload.| -|`responseCode`|string|Required|HTTP status code received| -|`responseBody`|string|Required|HTTP response body| -|`responseHeaders`|string|Required|HTTP response headers (Android)| +| Name | Type | Required | Description | +| ----------------- | ------ | -------- | ------------------------------- | +| `id` | string | Required | The ID of the upload. | +| `responseCode` | string | Required | HTTP status code received | +| `responseBody` | string | Required | HTTP response body | +| `responseHeaders` | string | Required | HTTP response headers (Android) | ### cancelled Event Data -|Name|Type|Required|Description| -|---|---|---|---| -|`id`|string|Required|The ID of the upload.| +| Name | Type | Required | Description | +| ---- | ------ | -------- | --------------------- | +| `id` | string | Required | The ID of the upload. | # Customizing Android Build Properties -You may want to customize the `compileSdk, buildToolsVersion, and targetSdkVersion` versions used by this package. For that, add this to `android/build.gradle`: + +You may want to customize the `compileSdk, buildToolsVersion, and targetSdkVersion` versions used by this package. For that, add this to `android/build.gradle`: ``` ext { @@ -276,7 +322,8 @@ ext { } ``` -Add it above `allProjects` and you're good. Your `android/build.gradle` might then resemble: +Add it above `allProjects` and you're good. Your `android/build.gradle` might then resemble: + ``` buildscript { repositories { @@ -308,12 +355,11 @@ Does it support iOS camera roll assets? Does it support multiple file uploads? -> Yes and No. It supports multiple concurrent uploads, but only a single upload per request. That should be fine for 90%+ of cases. +> Yes and No. It supports multiple concurrent uploads, but only a single upload per request. That should be fine for 90%+ of cases. Why should I use this file uploader instead of others that I've Googled like [react-native-uploader](https://github.com/aroth/react-native-uploader)? -> This package has two killer features not found anywhere else (as of 12/16/2016). First, it works on both iOS and Android. Others are iOS only. Second, it supports background uploading. This means that users can background your app and the upload will continue. This does not happen with other uploaders. - +> This package has two killer features not found anywhere else (as of 12/16/2016). First, it works on both iOS and Android. Others are iOS only. Second, it supports background uploading. This means that users can background your app and the upload will continue. This does not happen with other uploaders. # Contributing @@ -322,19 +368,22 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md). # Common Issues ## BREAKING CHANGE IN 3.0 -This is for 3.0 only. This does NOT apply to 4.0, as recent React Native versions have upgraded the `okhttp` dependencies. Anyway... + +This is for 3.0 only. This does NOT apply to 4.0, as recent React Native versions have upgraded the `okhttp` dependencies. Anyway... In 3.0, you need to add + ```gradle configurations.all { resolutionStrategy.force 'com.squareup.okhttp3:okhttp:3.4.1' } ``` + to your app's app's `android/app/build.gradle` file. Just add it above (not within) `dependencies` and you'll be fine. - ## BREAKING CHANGE IN 2.0 -Two big things happened in version 2.0. First, the Android package name had to be changed, as it conflicted with our own internal app. My bad. Second, we updated the android upload service dependency to the latest, but that requires the app have a compileSdkVersion and targetSdkVersion or 25. + +Two big things happened in version 2.0. First, the Android package name had to be changed, as it conflicted with our own internal app. My bad. Second, we updated the android upload service dependency to the latest, but that requires the app have a compileSdkVersion and targetSdkVersion or 25. To upgrade: In `MainApplication.java`: @@ -356,10 +405,10 @@ Ensure `compileSdkVersion` and `targetSdkVersion` are 25. Done! - ## Gratitude Thanks to: -- [android-upload-service](https://github.com/gotev/android-upload-service) It made Android dead simple to support. -- [MIME type from path on iOS](http://stackoverflow.com/questions/2439020/wheres-the-iphone-mime-type-database) Thanks for the answer! +- [android-upload-service](https://github.com/gotev/android-upload-service) It made Android dead simple to support. + +- [MIME type from path on iOS](http://stackoverflow.com/questions/2439020/wheres-the-iphone-mime-type-database) Thanks for the answer! diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..da9702f9 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/src/main/java/com/vydia/RNUploader/UploaderModule.kt b/android/src/main/java/com/vydia/RNUploader/UploaderModule.kt index 0258e95c..805b3212 100644 --- a/android/src/main/java/com/vydia/RNUploader/UploaderModule.kt +++ b/android/src/main/java/com/vydia/RNUploader/UploaderModule.kt @@ -126,7 +126,24 @@ class UploaderModule(val reactContext: ReactApplicationContext) : ReactContextBa */ @ReactMethod fun startUpload(options: ReadableMap, promise: Promise) { - for (key in arrayOf("url", "path")) { + val mandatoryParamsList: MutableList = mutableListOf("url") + if (options.hasKey("files")) { + if (options.getType("files") != ReadableType.Array) { + promise.reject(java.lang.IllegalArgumentException("files must be an array.")) + return + } + val files = options.getArray("files") + for (i in 0 until files!!.size()) { + val file = files.getMap(i) + if (!file.hasKey("path")) { + promise.reject(java.lang.IllegalArgumentException("Missing path field in files")) + return + } + } + } else { + mandatoryParamsList.add("path") + } + for (key in mandatoryParamsList) { if (!options.hasKey(key)) { promise.reject(java.lang.IllegalArgumentException("Missing '$key' field.")) return @@ -146,7 +163,10 @@ class UploaderModule(val reactContext: ReactApplicationContext) : ReactContextBa } configureUploadServiceHTTPStack(options, promise) var requestType: String? = "raw" - if (options.hasKey("type")) { + val files = options.getArray("files") + if (files != null) { + requestType = "multipart"; + } else if (options.hasKey("type")) { requestType = options.getString("type") if (requestType == null) { promise.reject(java.lang.IllegalArgumentException("type must be string.")) @@ -186,7 +206,14 @@ class UploaderModule(val reactContext: ReactApplicationContext) : ReactContextBa val maxRetries = if (options.hasKey("maxRetries") && options.getType("maxRetries") == ReadableType.Number) options.getInt("maxRetries") else 2 val customUploadId = if (options.hasKey("customUploadId") && options.getType("method") == ReadableType.String) options.getString("customUploadId") else null try { - val request = if (requestType == "raw") { + val request = if (files != null) { + var request = MultipartUploadRequest(this.reactApplicationContext, url!!) + for (i in 0 until files.size()) { + val file = files.getMap(i) + request = request.addFileToUpload(file.getString("path")!!, file.getString("field") ?: "file${i}") + } + request + } else if (requestType == "raw") { BinaryUploadRequest(this.reactApplicationContext, url!!) .setFileToUpload(filePath!!) } else { @@ -238,11 +265,23 @@ class UploaderModule(val reactContext: ReactApplicationContext) : ReactContextBa val keys = parameters!!.keySetIterator() while (keys.hasNextKey()) { val key = keys.nextKey() - if (parameters.getType(key) != ReadableType.String) { - promise.reject(java.lang.IllegalArgumentException("Parameters must be string key/values. Value was invalid for '$key'")) + if (parameters.getType(key) != ReadableType.String && parameters.getType(key) != ReadableType.Array) { + promise.reject(java.lang.IllegalArgumentException("Parameters must be string key/values or array key/List. Value was invalid for '$key'")) return } - request.addParameter(key, parameters.getString(key)!!) + if(parameters.getType(key) == ReadableType.String){ + request.addParameter(key, parameters.getString(key)!!) + }else{ + val valuesParams = parameters.getArray(key)!! + val convertedValue = mutableListOf() + for(i in 0 until valuesParams.size()){ + val str = valuesParams.getString(i) + println("Log: $str") + convertedValue.add(str) + } + request.addArrayParameter(key, convertedValue) + } + } } if (options.hasKey("headers")) { diff --git a/index.d.ts b/index.d.ts index 8b2a07ce..b53c25e4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -81,6 +81,15 @@ declare module "react-native-background-upload" { export interface UploadOptions { url: string; path: string; + /** + * Android only, to upload multiple files in one request via multipart + * `path` & `field` will be ignored and `type` will be multipart if `files` is available + */ + files?: { + path: string; + // Fallback to file0, file1, file2 etc + field?: string; + }; type?: 'raw' | 'multipart'; method?: 'POST' | 'GET' | 'PUT' | 'PATCH' | 'DELETE'; customUploadId?: string;