diff --git a/RNTusClient.podspec b/RNTusClient.podspec index b855287..98a1a5f 100644 --- a/RNTusClient.podspec +++ b/RNTusClient.podspec @@ -15,5 +15,5 @@ Pod::Spec.new do |s| s.ios.deployment_target = '8.0' s.dependency 'React-Core' - s.dependency 'TUSKit' + s.dependency 'TUSKit', '1.4.2' end diff --git a/android/build.gradle b/android/build.gradle index 509d7c6..ed6bfa6 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -34,8 +34,7 @@ repositories { } dependencies { - compile 'com.facebook.react:react-native:+' - compile group: 'org.jetbrains', name: 'annotations', version: '13.0' + implementation 'com.facebook.react:react-native:+' implementation 'io.tus.java.client:tus-java-client:0.4.2' implementation 'io.tus.android.client:tus-android-client:0.1.9' } diff --git a/android/src/main/java/com/vinzscam/rntusclient/RNTusClientModule.java b/android/src/main/java/com/vinzscam/rntusclient/RNTusClientModule.java index b047dd7..4c4c8d2 100644 --- a/android/src/main/java/com/vinzscam/rntusclient/RNTusClientModule.java +++ b/android/src/main/java/com/vinzscam/rntusclient/RNTusClientModule.java @@ -2,6 +2,7 @@ package com.vinzscam.rntusclient; import android.content.SharedPreferences; +import android.net.Uri; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactApplicationContext; @@ -12,6 +13,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; +import java.lang.Exception; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -19,6 +21,8 @@ import java.net.URL; import java.util.HashMap; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -28,10 +32,10 @@ import java.util.concurrent.atomic.AtomicBoolean; import io.tus.android.client.TusPreferencesURLStore; +import io.tus.android.client.TusAndroidUpload; import io.tus.java.client.ProtocolException; import io.tus.java.client.TusClient; import io.tus.java.client.TusExecutor; -import io.tus.java.client.TusUpload; import io.tus.java.client.TusUploader; public class RNTusClientModule extends ReactContextBaseJavaModule { @@ -63,31 +67,28 @@ public void createUpload(String fileUrl, ReadableMap options, Callback callback) Map rawMetadata = options.getMap("metadata").toHashMap(); Map metadata = new HashMap<>(); - for(String key: rawMetadata.keySet()) { + for (String key : rawMetadata.keySet()) { metadata.put(key, String.valueOf(rawMetadata.get(key))); } Map headers = new HashMap<>(); - for(String key: rawHeaders.keySet()) { + for (String key : rawHeaders.keySet()) { headers.put(key, String.valueOf(rawHeaders.get(key))); } - File file = new File(fileUrl); - TusUpload upload = null; try { - upload = new TusUpload(file); String uploadId = UUID.randomUUID().toString(); TusRunnable executor = new TusRunnable(fileUrl, uploadId, endpoint, metadata, headers); this.executorsMap.put(uploadId, executor); callback.invoke(uploadId); - } catch(FileNotFoundException | MalformedURLException e) { - callback.invoke((Object)null, e.getMessage()); + } catch (Exception e) { + callback.invoke((Object) null, e.getMessage()); } } @ReactMethod public void resume(String uploadId, Callback callback) { TusRunnable executor = this.executorsMap.get(uploadId); - if(executor != null) { + if (executor != null) { pool.submit(executor); callback.invoke(true); } else { @@ -99,39 +100,38 @@ public void resume(String uploadId, Callback callback) { public void abort(String uploadId, Callback callback) { try { TusRunnable executor = this.executorsMap.get(uploadId); - if(executor != null) { + if (executor != null) { executor.finish(); } - callback.invoke((Object)null); - } catch(IOException | ProtocolException e) { + callback.invoke((Object) null); + } catch (Exception e) { callback.invoke(e); } } class TusRunnable extends TusExecutor implements Runnable { - private TusUpload upload; + private TusAndroidUpload upload; private TusUploader uploader; private String uploadId; private TusClient client; private boolean shouldFinish; private boolean isRunning; - public TusRunnable(String fileUrl, - String uploadId, - String endpoint, - Map metadata, - Mapheaders - ) throws FileNotFoundException, MalformedURLException { + public TusRunnable(String fileUrl, String uploadId, String endpoint, Map metadata, + Map headers) throws FileNotFoundException, MalformedURLException { this.uploadId = uploadId; + client = new TusClient(); client.setUploadCreationURL(new URL(endpoint)); - SharedPreferences pref = getReactApplicationContext().getSharedPreferences("tus", 0); + SharedPreferences pref = reactContext.getSharedPreferences("tus", 0); + client.enableResuming(new TusPreferencesURLStore(pref)); client.setHeaders(headers); - File file = new File(fileUrl); - upload = new TusUpload((file)); + + upload = new TusAndroidUpload(Uri.parse(fileUrl), reactContext); upload.setMetadata(metadata); + shouldFinish = false; isRunning = false; } @@ -141,26 +141,38 @@ protected void makeAttempt() throws ProtocolException, IOException { uploader.setChunkSize(1024); uploader.setRequestPayloadSize(10 * 1024 * 1024); - do { - long totalBytes = upload.getSize(); - long bytesUploaded = uploader.getOffset(); - WritableMap params = Arguments.createMap(); - params.putString("uploadId", uploadId); - params.putDouble("bytesWritten", bytesUploaded); - params.putDouble("bytesTotal", totalBytes); - reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit(ON_PROGRESS, params); - }while(uploader.uploadChunk() > -1 && !shouldFinish); + Timer progressTicker = new Timer(); + + progressTicker.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + sendProgressEvent(upload.getSize(), uploader.getOffset()); + } + }, 0, 500); + do {} while (uploader.uploadChunk() > -1 && !shouldFinish); + + sendProgressEvent(upload.getSize(), upload.getSize()); + + progressTicker.cancel(); uploader.finish(); } + private void sendProgressEvent(long bytesTotal, long bytesUploaded) { + WritableMap params = Arguments.createMap(); + + params.putString("uploadId", uploadId); + params.putDouble("bytesWritten", bytesUploaded); + params.putDouble("bytesTotal", bytesTotal); + + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(ON_PROGRESS, params); + } + public void finish() throws ProtocolException, IOException { - if(isRunning) { + if (isRunning) { shouldFinish = true; - } - else { - if(uploader!= null){ + } else { + if (uploader != null) { uploader.finish(); } } @@ -176,16 +188,14 @@ public void run() { WritableMap params = Arguments.createMap(); params.putString("uploadId", uploadId); params.putString("uploadUrl", uploadUrl); - reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit(ON_SUCCESS, params); - } catch (ProtocolException | IOException e) { + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(ON_SUCCESS, params); + } catch (Exception e) { WritableMap params = Arguments.createMap(); params.putString("uploadId", uploadId); params.putString("error", e.toString()); - reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit(ON_ERROR, params); + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(ON_ERROR, params); } isRunning = false; } } -} \ No newline at end of file +}