diff --git a/README.md b/README.md index 394c33e..a6fdc04 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,53 @@ const uploadResult = await backgroundUpload( ); ``` +### Cancel Background Upload +for cancellation Upload, there is two ways +1. by calling, cancelUpload function +2. by calling abort function + +##### cancelUpload (support single and all) +```js +import { cancelUpload, backgroundUpload } from 'react-native-compressor'; + +// if we will call without passing any param then it will remove last pushed uploading +cancelUpload() + +// if you pass true as second param then it will cancel all the uploading +cancelUpload("",true) + +// if there is multiple files are uploading, and you wanna cancel specific uploading then you pass specific video id like this +let videoId='' +const uploadResult = await backgroundUpload( + url, + fileUrl, + { httpMethod: 'PUT', getCancellationId: (cancellationId) =>(videoId = cancellationId), }, + (written, total) => { + console.log(written, total); + } +); +cancelUpload(videoId) +``` + +##### cancel by calling abort +```js +import { backgroundUpload } from 'react-native-compressor'; + +const abortSignalRef = useRef(new AbortController()); + +const uploadResult = await backgroundUpload( + url, + fileUrl, + { httpMethod: 'PUT' }, + (written, total) => { + console.log(written, total); + }, + abortSignalRef.current.signal +); + +abortSignalRef.current?.abort(); // this will cancel uploading +``` + ### Download File ```js @@ -493,12 +540,24 @@ export declare type UploaderOptions = ( ) & { headers?: Record; httpMethod?: UploaderHttpMethod; + getCancellationId?: (cancellationId: string) => void; }; ``` **Note:** some of the uploader code is borrowed from [Expo](https://github.com/expo/expo) I tested file uploader on this backend [Nodejs-File-Uploader](https://github.com/numandev1/nodejs-file-uploader) +### Cancel Background Upload +- ##### cancelUpload: ( uuid?: string, shouldCancelAll?: boolean) => void + 1. If we call without passing any param then it will remove the last pushed uploading + 2. If you pass true as the second param then it will cancel all the uploading + 3. if there is multiple files are uploading, and you wanna cancel specific uploading then you pass a specific video ID like this + +- ##### we can use [AbortController](https://github.com/facebook/react-native/blob/255fef5263afdf9933ba2f8a3dbcbca39ea9928a/packages/react-native/types/modules/globals.d.ts#L531) in backgroundUpload [Usage](#cancel-background-upload) + `const abortSignalRef = useRef(new AbortController());` + + `abortSignalRef.current?.abort();` + ### Download - ##### download: ( fileUrl: string, downloadProgress?: (progress: number) => void, progressDivider?: number ) => Promise< string > diff --git a/android/src/main/java/com/reactnativecompressor/CompressorModule.kt b/android/src/main/java/com/reactnativecompressor/CompressorModule.kt index 558f452..c878392 100644 --- a/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +++ b/android/src/main/java/com/reactnativecompressor/CompressorModule.kt @@ -126,8 +126,8 @@ class CompressorModule(private val reactContext: ReactApplicationContext) : Comp } @ReactMethod - override fun cancelUpload() { - uploader.cancelUpload() + override fun cancelUpload(uuid: String,shouldCancelAll:Boolean) { + uploader.cancelUpload(uuid,shouldCancelAll) } @ReactMethod diff --git a/android/src/main/java/com/reactnativecompressor/Utils/HttpCallManager.kt b/android/src/main/java/com/reactnativecompressor/Utils/HttpCallManager.kt new file mode 100644 index 0000000..462df8d --- /dev/null +++ b/android/src/main/java/com/reactnativecompressor/Utils/HttpCallManager.kt @@ -0,0 +1,41 @@ +package com.reactnativecompressor.Utils + +import okhttp3.Call + +class HttpCallManager { + private var resumableCalls: MutableMap = HashMap() + + fun registerTask(call: Call, uuid: String) { + resumableCalls[uuid] = call + } + + fun taskForId(uuid: String): Call? { + return resumableCalls[uuid] + } + + // will use in future + fun downloadTaskForId(uuid: String): Call? { + return taskForId(uuid) + } + + fun uploadTaskForId(uuid: String): Call? { + return taskForId(uuid) + } + + fun taskPop(): Call? { + val lastUuid = resumableCalls.keys.lastOrNull() + val lastCall = resumableCalls.remove(lastUuid) + return lastCall + } + + fun unregisterTask(uuid: String) { + resumableCalls.remove(uuid) + } + + fun cancelAllTasks() { + for ((_, call) in resumableCalls) { + call?.cancel() + } + resumableCalls.clear() + } +} diff --git a/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt b/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt index 1f671d2..8fd7c6a 100644 --- a/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +++ b/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt @@ -29,7 +29,7 @@ class Uploader(private val reactContext: ReactApplicationContext) { val TAG = "asyncTaskUploader" var client: OkHttpClient? = null val MIN_EVENT_DT_MS: Long = 100 - private var currentCall: Call? = null + val httpCallManager = HttpCallManager() fun upload( fileUriString: String, @@ -59,12 +59,12 @@ class Uploader(private val reactContext: ReactApplicationContext) { okHttpClient?.let { val call = it.newCall(request) - currentCall = call + httpCallManager.registerTask(call,uuid) call.enqueue( object : Callback { override fun onFailure(call: Call, e: IOException) { Log.e(TAG, e.message.toString()) - promise.reject(TAG, e.message, e) + promise.reject(TAG, e.message) } override fun onResponse(call: Call, response: Response) { @@ -198,7 +198,18 @@ class Uploader(private val reactContext: ReactApplicationContext) { return responseHeaders } - fun cancelUpload() { - currentCall?.cancel() + fun cancelUpload(uuid:String,shouldCancelAll:Boolean) { + if(shouldCancelAll) + { + httpCallManager.cancelAllTasks() + } + else if(uuid=="") + { + httpCallManager.taskPop()?.cancel() + } + else + { + httpCallManager.uploadTaskForId(uuid)?.cancel() + } } } diff --git a/android/src/oldarch/CompressorSpec.kt b/android/src/oldarch/CompressorSpec.kt index 82f92d7..18ca452 100644 --- a/android/src/oldarch/CompressorSpec.kt +++ b/android/src/oldarch/CompressorSpec.kt @@ -23,7 +23,7 @@ abstract class CompressorSpec(context: ReactApplicationContext?) : ReactContextB abstract fun compress(fileUrl: String, optionMap: ReadableMap, promise: Promise) abstract fun cancelCompression(uuid: String) abstract fun upload(fileUrl: String, options: ReadableMap, promise: Promise) - abstract fun cancelUpload() + abstract fun cancelUpload(uuid: String, shouldCancelAll:Boolean) abstract fun download(fileUrl: String, options: ReadableMap, promise: Promise) abstract fun activateBackgroundTask(options: ReadableMap, promise: Promise) diff --git a/example/ios/CompressorExample.xcodeproj/project.pbxproj b/example/ios/CompressorExample.xcodeproj/project.pbxproj index ec5bca8..433f8d5 100644 --- a/example/ios/CompressorExample.xcodeproj/project.pbxproj +++ b/example/ios/CompressorExample.xcodeproj/project.pbxproj @@ -485,7 +485,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; INFOPLIST_FILE = CompressorExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -500,6 +503,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = CompressorExample; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -512,7 +516,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = CompressorExample/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -526,6 +533,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = CompressorExample; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; @@ -595,14 +603,14 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", - " ", + "-DRN_FABRIC_ENABLED", ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", - " ", + "-DRN_FABRIC_ENABLED", ); OTHER_LDFLAGS = ( "$(inherited)", @@ -673,14 +681,14 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = ( "$(inherited)", - " ", + "-DRN_FABRIC_ENABLED", ); OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DFOLLY_NO_CONFIG", "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", - " ", + "-DRN_FABRIC_ENABLED", ); OTHER_LDFLAGS = ( "$(inherited)", diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index bee0dcb..a1bb53e 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -880,7 +880,7 @@ PODS: - React-Codegen - React-RCTFabric - ReactCommon/turbomodule/core - - react-native-compressor (1.8.16): + - react-native-compressor (1.8.17): - hermes-engine - RCT-Folly (= 2021.07.22.00) - RCTRequired @@ -1377,7 +1377,7 @@ SPEC CHECKSUMS: React-jsinspector: 194e32c6aab382d88713ad3dd0025c5f5c4ee072 React-logger: cebf22b6cf43434e471dc561e5911b40ac01d289 react-native-cameraroll: 5d9523136a929b58f092fd7f0a9a13367a4b46e3 - react-native-compressor: 3ad769f5bac56d7337df076a1c62e74292f7136d + react-native-compressor: 8b6e302c4531f93aeaabd9658495bf9855adf3bd react-native-document-picker: c9ac93d7b511413f4a0ed61c92ff6c7b1bcf4f94 react-native-get-random-values: dee677497c6a740b71e5612e8dbd83e7539ed5bb react-native-image-picker: 9b4b1d0096500050cbdabf8f4fd00b771065d983 diff --git a/example/src/Screens/Video/index.tsx b/example/src/Screens/Video/index.tsx index 7dbaf91..ad0c650 100644 --- a/example/src/Screens/Video/index.tsx +++ b/example/src/Screens/Video/index.tsx @@ -12,6 +12,7 @@ import { Video, getRealPath, backgroundUpload, + cancelUpload, UploadType, UploaderHttpMethod, createVideoThumbnail, @@ -25,11 +26,14 @@ import { getFileInfo } from '../../Utils'; import ProgressBar from '../../Components/ProgressBar'; import type { ProgressBarRafType } from '../../Components/ProgressBar'; // const DOMAIN = 'http://localhost:8080'; -const DOMAIN = 'http://192.168.1.3:8080'; +const DOMAIN = 'http://192.168.1.5:8080'; const uploadPutRequest = `${DOMAIN}/upload/putRequestFile.mov`; +const uploadPutRequest1 = `${DOMAIN}/upload/putRequestFile1.mov`; const uploadPostRequest = `${DOMAIN}/upload`; // const uploadPostRequestFail = `${DOMAIN}/uploadFail`; +let counter1 = 0; export default function App() { + const cancellationIdForUploadRef = useRef(''); const progressRef = useRef(); const abortSignalRef = useRef(new AbortController()); const cancellationIdRef = useRef(''); @@ -45,6 +49,7 @@ export default function App() { const [backgroundMode, setBackgroundMode] = useState(false); useEffect(() => { + counter1 = -1; if (!sourceVideo) return; createVideoThumbnail(sourceVideo, {}) .then((response) => setSourceVideoThumbnail(response.path)) @@ -242,13 +247,16 @@ export default function App() { const headers = { Authorization: `Bearer ABCABC`, }; + counter1++; const result = await backgroundUpload( - uploadPutRequest, + counter1 % 2 == 0 ? uploadPutRequest : uploadPutRequest1, localFileUrl, { uploadType: UploadType.BINARY_CONTENT, httpMethod: UploaderHttpMethod.PUT, headers, + getCancellationId: (cancellationId) => + (cancellationIdForUploadRef.current = cancellationId), }, (written, total) => { progressRef.current?.setProgress(written / total); @@ -265,6 +273,11 @@ export default function App() { } }; + const cancelUploader = () => { + console.log('cancelUploader', cancellationIdForUploadRef.current); + cancelUpload(cancellationIdForUploadRef.current); + }; + const onCompressVideofromCameraoll = async () => { const photos = await CameraRoll.getPhotos({ first: 1, @@ -314,6 +327,8 @@ export default function App() { title="Upload(Put)" onPress={() => uploadByPutRequest('actual')} /> + +