From f7163fee2f1cb34a443346868fc1d86889283173 Mon Sep 17 00:00:00 2001 From: Austin Metka Date: Wed, 22 Feb 2023 13:12:11 -0500 Subject: [PATCH 1/5] Unzip when complete - > Add James Morton PR updates to this repo --- .../com/eko/RNBackgroundDownloaderModule.java | 85 +++++++++++++++++++ ...Downloader.m => RNBackgroundDownloader.mm} | 35 +++++--- react-native-background-downloader.podspec | 1 + 3 files changed, 109 insertions(+), 12 deletions(-) rename ios/{RNBackgroundDownloader.m => RNBackgroundDownloader.mm} (92%) diff --git a/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java b/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java index d772c86c..56cd03f0 100644 --- a/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java +++ b/android/src/main/java/com/eko/RNBackgroundDownloaderModule.java @@ -1,8 +1,10 @@ package com.eko; import android.annotation.SuppressLint; +import android.os.AsyncTask; import android.util.Log; + import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; @@ -31,8 +33,10 @@ import org.jetbrains.annotations.NotNull; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; @@ -41,6 +45,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import javax.annotation.Nullable; @@ -343,6 +349,49 @@ public void onCompleted(Download download) { params.putString("location", config.destination); ee.emit("downloadComplete", params); + final String filePath = download.getFile(); + final String taskId = config.id; + Log.d(getName(), "success callback!"); + + new Thread(new Runnable() { + @Override + public void run() { + try { + File downloadedZipFile = new File(filePath); + Log.d(getName(), "About to unzip: " + filePath + " (" + downloadedZipFile.exists() + ")"); + + File tempZipFile = new File(filePath + "_todelete"); + if (tempZipFile.exists()) { + boolean deleteResult = tempZipFile.delete(); + + if (!deleteResult) { + throw new Exception("File not deleted"); + } + } + boolean renameResult = downloadedZipFile.renameTo(tempZipFile); + if (!renameResult) { + throw new Exception("File not renamed"); + } + unzip(tempZipFile, downloadedZipFile); + boolean deleteResult = tempZipFile.delete(); + + if (!deleteResult) { + Log.d(getName(), "Temp zip file not deleted"); + } + + WritableMap params = Arguments.createMap(); + params.putString("id", taskId); + ee.emit("downloadComplete", params); + } catch(Exception e) { + e.printStackTrace(); + + WritableMap params = Arguments.createMap(); + params.putString("id", taskId); + params.putInt("errorcode", -1); + ee.emit("downloadFailed", params); + } + } + }).start(); } removeFromMaps(download.getId()); @@ -484,4 +533,40 @@ public void onDownloadBlockUpdated(Download download, DownloadBlock downloadBloc @Override public void onStarted(Download download, List list, int i) { } + + + private void unzip(File zipFile, File targetDirectory) throws IOException { + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile))); + + try { + ZipEntry ze; + int count; + byte[] buffer = new byte[8192]; + + while ((ze = zis.getNextEntry()) != null) { + File file = new File(targetDirectory, ze.getName()); + File dir = ze.isDirectory() ? file : file.getParentFile(); + + if (!dir.isDirectory() && !dir.mkdirs()) { + throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath()); + } + + if (ze.isDirectory()) { + continue; + } + + FileOutputStream fout = new FileOutputStream(file); + + try { + while ((count = zis.read(buffer)) != -1) { + fout.write(buffer, 0, count); + } + } finally { + fout.close(); + } + } + } finally { + zis.close(); + } + } } diff --git a/ios/RNBackgroundDownloader.m b/ios/RNBackgroundDownloader.mm similarity index 92% rename from ios/RNBackgroundDownloader.m rename to ios/RNBackgroundDownloader.mm index 0d25d202..40aefbce 100644 --- a/ios/RNBackgroundDownloader.m +++ b/ios/RNBackgroundDownloader.mm @@ -8,6 +8,7 @@ // #import "RNBackgroundDownloader.h" #import "RNBGDTaskConfig.h" +#import "SSZipArchive.h" #define ID_TO_CONFIG_MAP_KEY @"com.eko.bgdownloadidmap" @@ -324,18 +325,28 @@ - (void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSe @synchronized (sharedLock) { 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 (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": taskConfig.id, @"error": [error localizedDescription]}]; + // NSError *error = [self getServerError:downloadTask]; + // if (error == nil) { + // [self saveDownloadedFile:taskConfig downloadURL:location error:&error]; + // } + // if (self.bridge) { + // 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": taskConfig.id, @"error": [error localizedDescription]}]; + // } + // } + [SSZipArchive unzipFileAtPath:location.path toDestination:taskCofig.destination progressHandler:^(NSString *entry, unz_file_info zipInfo, long entryNumber, long total) { + } completionHandler:^(NSString *path, BOOL succeeded, NSError * _Nullable error) { + if (self.bridge) { + if (succeeded && error == nil) { + [self sendEventWithName:@"downloadComplete" body:@{@"id": taskCofig.id}]; + } else { + [self sendEventWithName:@"downloadFailed" body:@{@"id": taskCofig.id, @"error": [error localizedDescription]}]; + } } - } + }]; [self removeTaskFromMap:downloadTask]; } } @@ -427,4 +438,4 @@ - (id)deserialize: (NSData *)data { return [NSKeyedUnarchiver unarchiveObjectWithData:data]; } -@end +@end \ No newline at end of file diff --git a/react-native-background-downloader.podspec b/react-native-background-downloader.podspec index ef456726..8c0b2d44 100644 --- a/react-native-background-downloader.podspec +++ b/react-native-background-downloader.podspec @@ -14,4 +14,5 @@ Pod::Spec.new do |s| s.requires_arc = true s.dependency 'React-Core' + s.dependency 'SSZipArchive', '2.2.3' end From e5cb571134a9d87b7b73d26a4ffc418a31b70eb5 Mon Sep 17 00:00:00 2001 From: Austin Metka Date: Wed, 22 Feb 2023 17:20:22 -0500 Subject: [PATCH 2/5] rename --- ios/{RNBackgroundDownloader.mm => RNBackgroundDownloader.m} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ios/{RNBackgroundDownloader.mm => RNBackgroundDownloader.m} (100%) diff --git a/ios/RNBackgroundDownloader.mm b/ios/RNBackgroundDownloader.m similarity index 100% rename from ios/RNBackgroundDownloader.mm rename to ios/RNBackgroundDownloader.m From 913681a51cbe978d13303213e3acca32576c5776 Mon Sep 17 00:00:00 2001 From: Austin Metka Date: Thu, 23 Feb 2023 09:01:39 -0500 Subject: [PATCH 3/5] fix typo --- ios/RNBackgroundDownloader.m | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/ios/RNBackgroundDownloader.m b/ios/RNBackgroundDownloader.m index 40aefbce..1cb8d948 100644 --- a/ios/RNBackgroundDownloader.m +++ b/ios/RNBackgroundDownloader.m @@ -325,10 +325,10 @@ - (void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSe @synchronized (sharedLock) { RNBGDTaskConfig *taskConfig = taskToConfigMap[@(downloadTask.taskIdentifier)]; if (taskConfig != nil) { - // NSError *error = [self getServerError:downloadTask]; - // if (error == nil) { - // [self saveDownloadedFile:taskConfig downloadURL:location error:&error]; - // } + NSError *error = [self getServerError:downloadTask]; + if (error == nil) { + [self saveDownloadedFile:taskConfig downloadURL:location error:&error]; + } // if (self.bridge) { // if (error == nil) { // NSDictionary *responseHeaders = ((NSHTTPURLResponse *)downloadTask.response).allHeaderFields; @@ -337,13 +337,13 @@ - (void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSe // [self sendEventWithName:@"downloadFailed" body:@{@"id": taskConfig.id, @"error": [error localizedDescription]}]; // } // } - [SSZipArchive unzipFileAtPath:location.path toDestination:taskCofig.destination progressHandler:^(NSString *entry, unz_file_info zipInfo, long entryNumber, long total) { + [SSZipArchive unzipFileAtPath:location.path toDestination:taskConfig.destination progressHandler:^(NSString *entry, unz_file_info zipInfo, long entryNumber, long total) { } completionHandler:^(NSString *path, BOOL succeeded, NSError * _Nullable error) { if (self.bridge) { if (succeeded && error == nil) { - [self sendEventWithName:@"downloadComplete" body:@{@"id": taskCofig.id}]; + [self sendEventWithName:@"downloadComplete" body:@{@"id": taskConfig.id}]; } else { - [self sendEventWithName:@"downloadFailed" body:@{@"id": taskCofig.id, @"error": [error localizedDescription]}]; + [self sendEventWithName:@"downloadFailed" body:@{@"id": taskConfig.id, @"error": [error localizedDescription]}]; } } }]; @@ -359,26 +359,26 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTas - (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) { - // NSLog(@"[RNBackgroundDownloader] - [didWriteData] destination - %@", taskCofig.destination); - if (!taskCofig.reportedBegin) { + RNBGDTaskConfig *taskConfig = taskToConfigMap[@(downloadTask.taskIdentifier)]; + if (taskConfig != nil) { + // NSLog(@"[RNBackgroundDownloader] - [didWriteData] destination - %@", taskConfig.destination); + if (!taskConfig.reportedBegin) { NSDictionary *responseHeaders = ((NSHTTPURLResponse *)downloadTask.response).allHeaderFields; if (self.bridge) { [self sendEventWithName:@"downloadBegin" body:@{ - @"id": taskCofig.id, + @"id": taskConfig.id, @"expectedBytes": [NSNumber numberWithLongLong: totalBytesExpectedToWrite], @"headers": responseHeaders }]; } - taskCofig.reportedBegin = YES; + taskConfig.reportedBegin = YES; } - NSNumber *prevPercent = idToPercentMap[taskCofig.id]; + NSNumber *prevPercent = idToPercentMap[taskConfig.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; + progressReports[taskConfig.id] = @{@"id": taskConfig.id, @"written": [NSNumber numberWithLongLong: totalBytesWritten], @"total": [NSNumber numberWithLongLong: totalBytesExpectedToWrite], @"percent": percent}; + idToPercentMap[taskConfig.id] = percent; } NSDate *now = [[NSDate alloc] init]; @@ -399,12 +399,12 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didComp if (error == nil) return; - RNBGDTaskConfig *taskCofig = taskToConfigMap[@(task.taskIdentifier)]; - if (taskCofig == nil) + RNBGDTaskConfig *taskConfig = taskToConfigMap[@(task.taskIdentifier)]; + if (taskConfig == nil) return; if (self.bridge) { - [self sendEventWithName:@"downloadFailed" body:@{@"id": taskCofig.id, @"error": [error localizedDescription]}]; + [self sendEventWithName:@"downloadFailed" body:@{@"id": taskConfig.id, @"error": [error localizedDescription]}]; } // IF WE CAN'T RESUME TO DOWNLOAD LATER if (error.userInfo[NSURLSessionDownloadTaskResumeData] == nil) { From a8808c6053073637094bf50f540d20a1ae10d706 Mon Sep 17 00:00:00 2001 From: Austin Metka Date: Thu, 23 Feb 2023 14:01:35 -0500 Subject: [PATCH 4/5] add && self.bridge --- ios/RNBackgroundDownloader.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/RNBackgroundDownloader.m b/ios/RNBackgroundDownloader.m index 1cb8d948..5b0db3d9 100644 --- a/ios/RNBackgroundDownloader.m +++ b/ios/RNBackgroundDownloader.m @@ -362,7 +362,7 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTas RNBGDTaskConfig *taskConfig = taskToConfigMap[@(downloadTask.taskIdentifier)]; if (taskConfig != nil) { // NSLog(@"[RNBackgroundDownloader] - [didWriteData] destination - %@", taskConfig.destination); - if (!taskConfig.reportedBegin) { + if (!taskConfig.reportedBegin && self.bridge) { NSDictionary *responseHeaders = ((NSHTTPURLResponse *)downloadTask.response).allHeaderFields; if (self.bridge) { [self sendEventWithName:@"downloadBegin" body:@{ From f11c825d17a74a85d55b56b95d846098b5f72ce5 Mon Sep 17 00:00:00 2001 From: Austin Metka Date: Thu, 23 Feb 2023 14:01:49 -0500 Subject: [PATCH 5/5] update react-native version --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index c51f46a7..348c0141 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "lint-staged": ">=13", "metro-react-native-babel-preset": "^0.74.1", "react": "18.2.0", - "react-native": "0.71.1", + "react-native": "0.71.2", "react-native-fs": "^2.20.0", "react-native-vector-icons": "^9.2.0", "react-test-renderer": "18.2.0" diff --git a/yarn.lock b/yarn.lock index 6847bf70..486446f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6795,10 +6795,10 @@ react-native-fs@^2.20.0: base-64 "^0.1.0" utf8 "^3.0.0" -react-native-gradle-plugin@^0.71.13: - version "0.71.13" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.13.tgz#6f60ff24ac712554903dfc0ae98475cb280c57a6" - integrity sha512-C66LNZAXbU0YDRkWx8d/8kjesdu7fsUAc/3QPJNftSXKEvEtnFZK2aH/rIgu1s5dbTcE0fjhdVPNJMRIfKo61w== +react-native-gradle-plugin@^0.71.14: + version "0.71.15" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.15.tgz#9e6b506f30729fe8eb086981702f4e3c891d2b13" + integrity sha512-7S3pAuPaQJlhax6EZ4JMsDNpj05TfuzX9gPgWLrFfAIWIFLuJ6aDQYAZy2TEI9QJALPoWrj8LWaqP/DGYh14pw== react-native-vector-icons@^9.2.0: version "9.2.0" @@ -6808,10 +6808,10 @@ react-native-vector-icons@^9.2.0: prop-types "^15.7.2" yargs "^16.1.1" -react-native@0.71.1: - version "0.71.1" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.1.tgz#72b45af2b29e3d5a660c63425ab5003bf2112f99" - integrity sha512-bLP5+IBj2IX6tgF9WnC/UL2ZPYkVUPsU4xqZV1jntTC2TH4xyLrvfKACjGlz5nQ3Mx4BmOFqsnMxithm53+6Aw== +react-native@0.71.2: + version "0.71.2" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.2.tgz#b6977eda2a6dc10baa006bf4ab1ee08318607ce9" + integrity sha512-ZSianM+j+09LoEdVIhrAP/uP8sQhT7dH6olCqM2xlpxmfCgA5NubsK6NABIuZiBlmmqjigyijm5Y/GhBIHDvEg== dependencies: "@jest/create-cache-key-function" "^29.2.1" "@react-native-community/cli" "10.1.3" @@ -6838,7 +6838,7 @@ react-native@0.71.1: promise "^8.3.0" react-devtools-core "^4.26.1" react-native-codegen "^0.71.3" - react-native-gradle-plugin "^0.71.13" + react-native-gradle-plugin "^0.71.14" react-refresh "^0.4.0" react-shallow-renderer "^16.15.0" regenerator-runtime "^0.13.2"