upd from local changes to work correctly

This commit is contained in:
Kesha Antonov
2021-12-05 20:48:08 +03:00
parent a1769b4a91
commit 7012fed52d
6 changed files with 259 additions and 71 deletions
+7 -7
View File
@@ -92,13 +92,13 @@ let task = RNBackgroundDownloader.download({
id: 'file123',
url: 'https://link-to-very.large/file.zip'
destination: `${RNBackgroundDownloader.directories.documents}/file.zip`
}).begin((expectedBytes) => {
}).begin(({ expectedBytes, headers }) => {
console.log(`Going to download ${expectedBytes} bytes!`);
}).progress((percent) => {
}).progress(percent => {
console.log(`Downloaded: ${percent * 100}%`);
}).done(() => {
console.log('Download is done!');
}).error((error) => {
}).error(error => {
console.log('Download canceled due to error: ', error);
});
@@ -126,7 +126,7 @@ import RNBackgroundDownloader from 'react-native-background-downloader';
let lostTasks = await RNBackgroundDownloader.checkForExistingDownloads();
for (let task of lostTasks) {
console.log(`Task ${task.id} was found!`);
task.progress((percent) => {
task.progress(percent => {
console.log(`Downloaded: ${percent * 100}%`);
}).done(() => {
console.log('Downlaod is done!');
@@ -158,9 +158,9 @@ let task = RNBackgroundDownloader.download({
headers: {
Authorization: 'Bearer 2we$@$@Ddd223'
}
}).begin((expectedBytes) => {
}).begin(({ expectedBytes, headers }) => {
console.log(`Going to download ${expectedBytes} bytes!`);
}).progress((percent) => {
}).progress(percent => {
console.log(`Downloaded: ${percent * 100}%`);
}).done(() => {
console.log('Download is done!');
@@ -228,7 +228,7 @@ All callback methods return the current instance of the `DownloadTask` for chain
| Function | Callback Arguments | Info |
| ---------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `begin` | expectedBytes | Called when the first byte is received. 💡: this is good place to check if the device has enough storage space for this download |
| `begin` | { expectedBytes, headers } | Called when the first byte is received. 💡: this is good place to check if the device has enough storage space for this download |
| `progress` | percent, bytesWritten, totalBytes | Called at max every 1.5s so you can update your progress bar accordingly |
| `done` | | Called when the download is done, the file is at the destination you've set |
| `error` | error | Called when the download stops due to an error |
+7 -5
View File
@@ -5,11 +5,11 @@ def safeExtGet(prop, fallback) {
}
android {
compileSdkVersion safeExtGet("compileSdkVersion", 28)
compileSdkVersion safeExtGet("compileSdkVersion", 30)
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 16)
targetSdkVersion safeExtGet('targetSdkVersion', 28)
minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 30)
versionCode 1
versionName "1.0"
ndk {
@@ -22,8 +22,10 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+'
if (project.properties['android.useAndroidX'] == 'true' || project.properties['android.useAndroidX'] == true) {
api "androidx.tonyodev.fetch2:xfetch2:3.1.4"
api "androidx.tonyodev.fetch2:xfetch2:3.1.6"
implementation "androidx.tonyodev.fetch2okhttp:xfetch2okhttp:3.1.6"
} else {
api "com.tonyodev.fetch2:fetch2:3.0.10"
api "com.tonyodev.fetch2:fetch2:3.0.12"
implementation "com.tonyodev.fetch2okhttp:fetch2okhttp:3.0.12"
}
}
@@ -14,6 +14,8 @@ import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.tonyodev.fetch2.Download;
import com.tonyodev.fetch2core.Downloader;
import com.tonyodev.fetch2okhttp.OkHttpDownloader;
import com.tonyodev.fetch2.Error;
import com.tonyodev.fetch2.Fetch;
import com.tonyodev.fetch2.FetchConfiguration;
@@ -24,6 +26,8 @@ import com.tonyodev.fetch2.Request;
import com.tonyodev.fetch2.Status;
import com.tonyodev.fetch2core.DownloadBlock;
import com.tonyodev.fetch2core.Func;
import com.tonyodev.fetch2.HttpUrlConnectionDownloader;
import com.tonyodev.fetch2core.Downloader;
import org.jetbrains.annotations.NotNull;
@@ -40,6 +44,12 @@ import java.util.Map;
import javax.annotation.Nullable;
import okhttp3.OkHttpClient;
import java.util.Set;
import java.net.URL;
import java.net.URLConnection;
public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule implements FetchListener {
private static final int TASK_RUNNING = 0;
@@ -72,15 +82,21 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
private DeviceEventManagerModule.RCTDeviceEventEmitter ee;
private Date lastProgressReport = new Date();
private HashMap<String, WritableMap> progressReports = new HashMap<>();
private static Object sharedLock = new Object();
private static Object sharedLock = new Object();
public RNBackgroundDownloaderModule(ReactApplicationContext reactContext) {
super(reactContext);
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
final Downloader okHttpDownloader = new OkHttpDownloader(okHttpClient,
Downloader.FileDownloaderType.PARALLEL);
loadConfigMap();
FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this.getReactApplicationContext())
.setDownloadConcurrentLimit(4)
.setNamespace("RNBackgroundDownloader")
.setHttpDownloader(okHttpDownloader)
.enableRetryOnNetworkGain(true)
.setHttpDownloader(new HttpUrlConnectionDownloader(Downloader.FileDownloaderType.PARALLEL))
.build();
fetch = Fetch.Impl.getInstance(fetchConfiguration);
fetch.addListener(this);
@@ -164,7 +180,7 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
e.printStackTrace();
}
}
private int convertErrorCode(Error error) {
if ((error == Error.FILE_NOT_CREATED)
|| (error == Error.WRITE_PERMISSION_DENIED)) {
@@ -205,7 +221,7 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
}
request.setPriority(options.hasKey("priority") ? Priority.valueOf(options.getInt("priority")) : Priority.NORMAL);
request.setNetworkType(options.hasKey("network") ? NetworkType.valueOf(options.getInt("network")) : NetworkType.ALL);
fetch.enqueue(request, new Func<Request>() {
@Override
public void call(Request download) {
@@ -214,7 +230,7 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
@Override
public void call(Error error) {
//An error occurred when enqueuing a request.
WritableMap params = Arguments.createMap();
params.putString("id", id);
params.putString("error", error.toString());
@@ -232,6 +248,7 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
);
synchronized(sharedLock) {
lastProgressReport = new Date();
idToRequestId.put(id, request.getId());
requestIdToConfig.put(request.getId(), config);
saveConfigMap();
@@ -288,6 +305,8 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
foundIds.pushMap(params);
// TODO: MAYBE ADD headers
idToRequestId.put(config.id, download.getId());
config.reportedBegin = true;
} else {
@@ -309,6 +328,9 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
if (config != null) {
WritableMap params = Arguments.createMap();
params.putString("id", config.id);
// TODO: add location
ee.emit("downloadComplete", params);
}
@@ -331,16 +353,44 @@ public class RNBackgroundDownloaderModule extends ReactContextBaseJavaModule imp
params.putString("id", config.id);
if (!config.reportedBegin) {
params.putInt("expectedBytes", (int)download.getTotal());
ee.emit("downloadBegin", params);
config.reportedBegin = true;
params.putInt("expectedBytes", (int)download.getTotal());
// TODO: MAKE IT IN CUSTOM DOWNLOADER
// https://github.com/tonyofrancis/Fetch/issues/347#issuecomment-478349299
Thread th = new Thread(() -> {
try {
WritableMap headersMap = Arguments.createMap();
URL urlC = new URL(download.getUrl());
URLConnection con = urlC.openConnection();
Map<String,List<String>> headers = con.getHeaderFields();
Set<String> keys = headers.keySet();
for (String key : keys) {
String val = con.getHeaderField(key);
headersMap.putString(key, val);
}
params.putMap("headers", headersMap);
ee.emit("downloadBegin", params);
} catch (IOException e) {
e.printStackTrace();
}
});
try {
th.start();
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
params.putInt("written", (int)download.getDownloaded());
params.putInt("total", (int)download.getTotal());
params.putDouble("percent", ((double)download.getProgress()) / 100);
progressReports.put(config.id, params);
Date now = new Date();
if (now.getTime() - lastProgressReport.getTime() > 1500) {
if (now.getTime() - lastProgressReport.getTime() > 250) {
WritableArray reportsArray = Arguments.createArray();
for (WritableMap report : progressReports.values()) {
reportsArray.pushMap(report);
+13 -8
View File
@@ -15,12 +15,12 @@ RNBackgroundDownloaderEmitter.addListener('downloadProgress', events => {
}
});
RNBackgroundDownloaderEmitter.addListener('downloadComplete', event => {
let task = tasksMap.get(event.id);
RNBackgroundDownloaderEmitter.addListener('downloadComplete', ({ id, location }) => {
let task = tasksMap.get(id);
if (task) {
task._onDone(event.location);
task._onDone({ location });
}
tasksMap.delete(event.id);
tasksMap.delete(id);
});
RNBackgroundDownloaderEmitter.addListener('downloadFailed', event => {
@@ -31,10 +31,10 @@ RNBackgroundDownloaderEmitter.addListener('downloadFailed', event => {
tasksMap.delete(event.id);
});
RNBackgroundDownloaderEmitter.addListener('downloadBegin', event => {
let task = tasksMap.get(event.id);
RNBackgroundDownloaderEmitter.addListener('downloadBegin', ({ id, expectedBytes, headers }) => {
let task = tasksMap.get(id);
if (task) {
task._onBegin(event.expectedBytes);
task._onBegin({ expectedBytes, headers });
}
});
@@ -49,7 +49,7 @@ export function checkForExistingDownloads() {
return RNBackgroundDownloader.checkForExistingDownloads()
.then(foundTasks => {
return foundTasks.map(taskInfo => {
let task = new DownloadTask(taskInfo);
let task = new DownloadTask(taskInfo,tasksMap.get(taskInfo.id));
if (taskInfo.state === RNBackgroundDownloader.TaskRunning) {
task.state = 'DOWNLOADING';
} else if (taskInfo.state === RNBackgroundDownloader.TaskSuspended) {
@@ -71,6 +71,10 @@ export function checkForExistingDownloads() {
});
}
export function completeHandler (jobId) {
return RNBackgroundDownloader.completeHandler(jobId)
}
export function download(options) {
if (!options.id || !options.url || !options.destination) {
throw new Error('[RNBackgroundDownloader] id, url and destination are required');
@@ -107,6 +111,7 @@ export const Priority = {
export default {
download,
checkForExistingDownloads,
completeHandler,
setHeaders,
directories,
Network,
+153 -29
View File
@@ -23,6 +23,7 @@ static CompletionHandler storedCompletionHandler;
NSMutableDictionary<NSString *, NSDictionary *> *progressReports;
NSDate *lastProgressReport;
NSNumber *sharedLock;
BOOL isNotificationCenterInited;
}
RCT_EXPORT_MODULE();
@@ -42,7 +43,7 @@ RCT_EXPORT_MODULE();
- (NSDictionary *)constantsToExport {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return @{
@"documents": [paths firstObject],
@"TaskRunning": @(NSURLSessionTaskStateRunning),
@@ -53,6 +54,7 @@ RCT_EXPORT_MODULE();
}
- (id) init {
NSLog(@"[RNBackgroundDownloader] - [init]");
self = [super init];
if (self) {
taskToConfigMap = [self deserialize:[[NSUserDefaults standardUserDefaults] objectForKey:ID_TO_CONFIG_MAP_KEY]];
@@ -65,6 +67,18 @@ RCT_EXPORT_MODULE();
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSString *sessonIdentifier = [bundleIdentifier stringByAppendingString:@".backgrounddownloadtask"];
sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessonIdentifier];
sessionConfig.HTTPMaximumConnectionsPerHost = 4;
sessionConfig.timeoutIntervalForRequest = 60 * 60; // MAX TIME TO GET NEW DATA IN REQUEST - 1 HOUR
sessionConfig.timeoutIntervalForResource = 60 * 60 * 24; // MAX TIME TO DOWNLOAD RESOURCE - 1 DAY
sessionConfig.discretionary = NO;
sessionConfig.sessionSendsLaunchEvents = YES;
if (@available(iOS 9.0, *)) {
sessionConfig.shouldUseExtendedBackgroundIdleMode = YES;
}
if (@available(iOS 13.0, *)) {
sessionConfig.allowsExpensiveNetworkAccess = YES;
}
progressReports = [[NSMutableDictionary alloc] init];
lastProgressReport = [[NSDate alloc] init];
sharedLock = [NSNumber numberWithInt:1];
@@ -73,12 +87,63 @@ RCT_EXPORT_MODULE();
}
- (void)lazyInitSession {
if (urlSession == nil) {
urlSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSLog(@"[RNBackgroundDownloader] - [lazyInitSession]");
@synchronized (sharedLock) {
if (urlSession == nil) {
urlSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
}
if (isNotificationCenterInited != YES) {
isNotificationCenterInited = YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(resumeTasks:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
}
}
- (void) dealloc {
NSLog(@"[RNBackgroundDownloader] - [dealloc]");
[urlSession invalidateAndCancel];
urlSession = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
// NOTE: FIXES HANGING DOWNLOADS WHEN GOING TO BG
- (void) resumeTasks:(NSNotification *) note {
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 1");
@synchronized (self->sharedLock) {
[urlSession getTasksWithCompletionHandler:^(NSArray<NSURLSessionDataTask *> * _Nonnull dataTasks, NSArray<NSURLSessionUploadTask *> * _Nonnull uploadTasks, NSArray<NSURLSessionDownloadTask *> * _Nonnull downloadTasks) {
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 2");
for (NSURLSessionDownloadTask *task in downloadTasks) {
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 3 - state - %@", [NSNumber numberWithInt:task.state]);
// running - 0
// suspended - 1
// canceling - 2
// completed - 3
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 4 - totalBytes - %@", [NSNumber numberWithInt:task.countOfBytesExpectedToReceive]);
NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 5 - bytesWritten - %@", [NSNumber numberWithInt:task.countOfBytesReceived]);
if (task.state == NSURLSessionTaskStateRunning) {
[task suspend]; // PAUSE
[task resume];
}
}
// TODO: MAYBE ADD FOR OTHER TASKS TYPES
// for (NSURLSessionDataTask *task in dataTasks) {
// NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 5");
// [task resume];
// }
// for (NSURLSessionUploadTask *task in dataTasks) {
// NSLog(@"[RNBackgroundDownloader] - [resumeTasks] 6");
// [task resume];
// }
}];
}
}
- (void)removeTaskFromMap: (NSURLSessionTask *)task {
NSLog(@"[RNBackgroundDownloader] - [removeTaskFromMap]");
@synchronized (sharedLock) {
NSNumber *taskId = @(task.taskIdentifier);
RNBGDTaskConfig *taskConfig = taskToConfigMap[taskId];
@@ -98,6 +163,7 @@ RCT_EXPORT_MODULE();
}
+ (void)setCompletionHandlerWithIdentifier: (NSString *)identifier completionHandler: (CompletionHandler)completionHandler {
NSLog(@"[RNBackgroundDownloader] - [setCompletionHandlerWithIdentifier]");
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSString *sessonIdentifier = [bundleIdentifier stringByAppendingString:@".backgrounddownloadtask"];
if ([sessonIdentifier isEqualToString:identifier]) {
@@ -105,9 +171,31 @@ RCT_EXPORT_MODULE();
}
}
- (NSError *)getServerError: (nonnull NSURLSessionDownloadTask *)downloadTask {
NSLog(@"[RNBackgroundDownloader] - [getServerError]");
NSError *serverError;
NSInteger httpStatusCode = [((NSHTTPURLResponse *)downloadTask.response) statusCode];
if(httpStatusCode != 200) {
serverError = [NSError errorWithDomain:NSURLErrorDomain
code:httpStatusCode
userInfo:@{NSLocalizedDescriptionKey: [NSHTTPURLResponse localizedStringForStatusCode: httpStatusCode]}];
}
return serverError;
}
- (BOOL)saveDownloadedFile: (nonnull RNBGDTaskConfig *) taskConfig downloadURL:(nonnull NSURL *)location error:(NSError **)saveError {
NSLog(@"[RNBackgroundDownloader] - [saveDownloadedFile]");
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *destURL = [NSURL fileURLWithPath:taskConfig.destination];
[fileManager createDirectoryAtURL:[destURL URLByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager removeItemAtURL:destURL error:nil];
return [fileManager moveItemAtURL:location toURL:destURL error:saveError];
}
#pragma mark - JS exported methods
RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
NSLog(@"[RNBackgroundDownloader] - [download]");
NSString *identifier = options[@"id"];
NSString *url = options[@"url"];
NSString *destination = options[@"destination"];
@@ -116,17 +204,22 @@ RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
NSLog(@"[RNBackgroundDownloader] - [Error] id, url and destination must be set");
return;
}
[self lazyInitSession];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
if (headers != nil) {
for (NSString *headerKey in headers) {
[request setValue:[headers valueForKey:headerKey] forHTTPHeaderField:headerKey];
}
}
@synchronized (sharedLock) {
[self lazyInitSession];
NSURLSessionDownloadTask __strong *task = [urlSession downloadTaskWithRequest:request];
if (task == nil) {
NSLog(@"[RNBackgroundDownloader] - [Error] failed to create download task");
return;
}
RNBGDTaskConfig *taskConfig = [[RNBGDTaskConfig alloc] initWithDictionary: @{@"id": identifier, @"destination": destination}];
taskToConfigMap[@(task.taskIdentifier)] = taskConfig;
@@ -134,12 +227,14 @@ RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
idToTaskMap[identifier] = task;
idToPercentMap[identifier] = @0.0;
[task resume];
lastProgressReport = [[NSDate alloc] init];
}
}
RCT_EXPORT_METHOD(pauseTask: (NSString *)identifier) {
NSLog(@"[RNBackgroundDownloader] - [pauseTask]");
@synchronized (sharedLock) {
NSURLSessionDownloadTask *task = idToTaskMap[identifier];
if (task != nil && task.state == NSURLSessionTaskStateRunning) {
@@ -149,6 +244,7 @@ RCT_EXPORT_METHOD(pauseTask: (NSString *)identifier) {
}
RCT_EXPORT_METHOD(resumeTask: (NSString *)identifier) {
NSLog(@"[RNBackgroundDownloader] - [resumeTask]");
@synchronized (sharedLock) {
NSURLSessionDownloadTask *task = idToTaskMap[identifier];
if (task != nil && task.state == NSURLSessionTaskStateSuspended) {
@@ -158,6 +254,7 @@ RCT_EXPORT_METHOD(resumeTask: (NSString *)identifier) {
}
RCT_EXPORT_METHOD(stopTask: (NSString *)identifier) {
NSLog(@"[RNBackgroundDownloader] - [stopTask]");
@synchronized (sharedLock) {
NSURLSessionDownloadTask *task = idToTaskMap[identifier];
if (task != nil) {
@@ -168,6 +265,7 @@ RCT_EXPORT_METHOD(stopTask: (NSString *)identifier) {
}
RCT_EXPORT_METHOD(checkForExistingDownloads: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
NSLog(@"[RNBackgroundDownloader] - [checkForExistingDownloads]");
[self lazyInitSession];
[urlSession getTasksWithCompletionHandler:^(NSArray<NSURLSessionDataTask *> * _Nonnull dataTasks, NSArray<NSURLSessionUploadTask *> * _Nonnull uploadTasks, NSArray<NSURLSessionDownloadTask *> * _Nonnull downloadTasks) {
NSMutableArray *idsFound = [[NSMutableArray alloc] init];
@@ -205,22 +303,37 @@ RCT_EXPORT_METHOD(checkForExistingDownloads: (RCTPromiseResolveBlock)resolve rej
}];
}
RCT_EXPORT_METHOD(completeHandler:(nonnull NSString *)jobId
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSLog(@"[RNBackgroundDownloader] - [completeHandlerIOS]");
if (storedCompletionHandler) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
storedCompletionHandler();
storedCompletionHandler = nil;
}];
}
resolve(nil);
}
#pragma mark - NSURLSessionDownloadDelegate methods
- (void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location {
NSLog(@"[RNBackgroundDownloader] - [didFinishDownloadingToURL]");
@synchronized (sharedLock) {
RNBGDTaskConfig *taskCofig = taskToConfigMap[@(downloadTask.taskIdentifier)];
if (taskCofig != nil) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *destURL = [NSURL fileURLWithPath:taskCofig.destination];
[fileManager createDirectoryAtURL:[destURL URLByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager removeItemAtURL:destURL error:nil];
NSError *moveError;
BOOL moved = [fileManager moveItemAtURL:location toURL:destURL error:&moveError];
RNBGDTaskConfig *taskConfig = taskToConfigMap[@(downloadTask.taskIdentifier)];
if (taskConfig != nil) {
NSError *error = [self getServerError:downloadTask];
if (error == nil) {
[self saveDownloadedFile:taskConfig downloadURL:location error:&error];
}
if (self.bridge) {
if (moved) {
[self sendEventWithName:@"downloadComplete" body:@{@"id": taskCofig.id}];
if (error == nil) {
NSDictionary *responseHeaders = ((NSHTTPURLResponse *)downloadTask.response).allHeaderFields;
[self sendEventWithName:@"downloadComplete" body:@{@"id": taskConfig.id, @"headers": responseHeaders, @"location": taskConfig.destination}];
} else {
[self sendEventWithName:@"downloadFailed" body:@{@"id": taskCofig.id, @"error": [moveError localizedDescription]}];
[self sendEventWithName:@"downloadFailed" body:@{@"id": taskConfig.id, @"error": [error localizedDescription]}];
}
}
[self removeTaskFromMap:downloadTask];
@@ -229,26 +342,33 @@ RCT_EXPORT_METHOD(checkForExistingDownloads: (RCTPromiseResolveBlock)resolve rej
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
NSLog(@"[RNBackgroundDownloader] - [didResumeAtOffset]");
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
NSLog(@"[RNBackgroundDownloader] - [didWriteData]");
@synchronized (sharedLock) {
RNBGDTaskConfig *taskCofig = taskToConfigMap[@(downloadTask.taskIdentifier)];
if (taskCofig != nil) {
if (!taskCofig.reportedBegin) {
[self sendEventWithName:@"downloadBegin" body:@{@"id": taskCofig.id, @"expectedBytes": [NSNumber numberWithLongLong: totalBytesExpectedToWrite]}];
NSDictionary *responseHeaders = ((NSHTTPURLResponse *)downloadTask.response).allHeaderFields;
[self sendEventWithName:@"downloadBegin" body:@{
@"id": taskCofig.id,
@"expectedBytes": [NSNumber numberWithLongLong: totalBytesExpectedToWrite],
@"headers": responseHeaders
}];
taskCofig.reportedBegin = YES;
}
NSNumber *prevPercent = idToPercentMap[taskCofig.id];
NSNumber *percent = [NSNumber numberWithFloat:(float)totalBytesWritten/(float)totalBytesExpectedToWrite];
if ([percent floatValue] - [prevPercent floatValue] > 0.01f) {
progressReports[taskCofig.id] = @{@"id": taskCofig.id, @"written": [NSNumber numberWithLongLong: totalBytesWritten], @"total": [NSNumber numberWithLongLong: totalBytesExpectedToWrite], @"percent": percent};
idToPercentMap[taskCofig.id] = percent;
}
NSDate *now = [[NSDate alloc] init];
if ([now timeIntervalSinceDate:lastProgressReport] > 1.5 && progressReports.count > 0) {
if ([now timeIntervalSinceDate:lastProgressReport] > 0.25 && progressReports.count > 0) {
if (self.bridge) {
[self sendEventWithName:@"downloadProgress" body:[progressReports allValues]];
}
@@ -260,6 +380,7 @@ RCT_EXPORT_METHOD(checkForExistingDownloads: (RCTPromiseResolveBlock)resolve rej
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"[RNBackgroundDownloader] - [didCompleteWithError]");
@synchronized (sharedLock) {
RNBGDTaskConfig *taskCofig = taskToConfigMap[@(task.taskIdentifier)];
if (error != nil && error.code != -999 && taskCofig != nil) {
@@ -272,12 +393,15 @@ RCT_EXPORT_METHOD(checkForExistingDownloads: (RCTPromiseResolveBlock)resolve rej
}
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (storedCompletionHandler) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
storedCompletionHandler();
storedCompletionHandler = nil;
}];
}
NSLog(@"[RNBackgroundDownloader] - [URLSessionDidFinishEventsForBackgroundURLSession]");
// USE completionHandler FROM JS INSTEAD OF THIS
// TOREMOVE
// if (storedCompletionHandler) {
// [[NSOperationQueue mainQueue] addOperationWithBlock:^{
// storedCompletionHandler();
// storedCompletionHandler = nil;
// }];
// }
}
#pragma mark - serialization
@@ -289,7 +413,7 @@ RCT_EXPORT_METHOD(checkForExistingDownloads: (RCTPromiseResolveBlock)resolve rej
if (data == nil) {
return nil;
}
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
+21 -14
View File
@@ -12,7 +12,7 @@ export default class DownloadTask {
bytesWritten = 0
totalBytes = 0
constructor(taskInfo) {
constructor (taskInfo, originalTask) {
if (typeof taskInfo === 'string') {
this.id = taskInfo;
} else {
@@ -21,40 +21,47 @@ export default class DownloadTask {
this.bytesWritten = taskInfo.bytesWritten;
this.totalBytes = taskInfo.totalBytes;
}
if (originalTask) {
this._beginHandler = originalTask._beginHandler
this._progressHandler = originalTask._progressHandler
this._doneHandler = originalTask._doneHandler
this._errorHandler = originalTask._errorHandler
}
}
begin(handler) {
begin (handler) {
validateHandler(handler);
this._beginHandler = handler;
return this;
}
progress(handler) {
progress (handler) {
validateHandler(handler);
this._progressHandler = handler;
return this;
}
done(handler) {
done (handler) {
validateHandler(handler);
this._doneHandler = handler;
return this;
}
error(handler) {
error (handler) {
validateHandler(handler);
this._errorHandler = handler;
return this;
}
_onBegin(expectedBytes) {
_onBegin ({ expectedBytes, headers }) {
this.state = 'DOWNLOADING';
if (this._beginHandler) {
this._beginHandler(expectedBytes);
this._beginHandler({ expectedBytes, headers });
}
}
_onProgress(percent, bytesWritten, totalBytes) {
_onProgress (percent, bytesWritten, totalBytes) {
this.percent = percent;
this.bytesWritten = bytesWritten;
this.totalBytes = totalBytes;
@@ -63,31 +70,31 @@ export default class DownloadTask {
}
}
_onDone() {
_onDone ({ location }) {
this.state = 'DONE';
if (this._doneHandler) {
this._doneHandler();
this._doneHandler({ location });
}
}
_onError(error, errorCode) {
_onError (error, errorCode) {
this.state = 'FAILED';
if (this._errorHandler) {
this._errorHandler(error, errorCode);
}
}
pause() {
pause () {
this.state = 'PAUSED';
RNBackgroundDownloader.pauseTask(this.id);
}
resume() {
resume () {
this.state = 'DOWNLOADING';
RNBackgroundDownloader.resumeTask(this.id);
}
stop() {
stop () {
this.state = 'STOPPED';
RNBackgroundDownloader.stopTask(this.id);
}