react-native-blob-util 0.22.0 → 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +56 -56
- package/LICENSE +21 -21
- package/Migration.md +41 -41
- package/NuGet.config +11 -11
- package/README.md +1109 -1104
- package/android/gradle.properties +1 -1
- package/android/src/main/AndroidManifest.xml +37 -37
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +57 -57
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConst.java +20 -20
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +9 -9
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilImpl.java +431 -431
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilProgressConfig.java +39 -39
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +1014 -1014
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilDefaultResp.java +98 -98
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +166 -166
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -19
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileProvider.java +4 -4
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -70
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/PathResolver.java +223 -223
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/Tls12SocketFactory.java +68 -68
- package/android/src/main/res/values/strings.xml +3 -3
- package/android/src/main/res/xml/provider_paths.xml +12 -12
- package/android.js +58 -58
- package/class/ReactNativeBlobUtilCanceledFetchError.js +9 -9
- package/class/ReactNativeBlobUtilFile.js +5 -5
- package/class/ReactNativeBlobUtilReadStream.js +84 -84
- package/class/ReactNativeBlobUtilSession.js +68 -68
- package/class/ReactNativeBlobUtilWriteStream.js +50 -50
- package/class/StatefulPromise.js +7 -7
- package/codegenSpecs/NativeBlobUtils.js +86 -86
- package/components/Fetch.onPress.js +10 -10
- package/components/Fetch.when.js +15 -15
- package/fetch.js +350 -350
- package/fs.js +480 -480
- package/index.js +64 -64
- package/index.js.flow +192 -192
- package/index.web.js +1 -1
- package/ios/PrivacyInfo.xcprivacy +28 -28
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.h +55 -55
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.mm +992 -992
- package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +405 -405
- package/ios/ReactNativeBlobUtilConst.h +58 -58
- package/ios/ReactNativeBlobUtilConst.mm +47 -47
- package/ios/ReactNativeBlobUtilFS.h +108 -108
- package/ios/ReactNativeBlobUtilFS.mm +1064 -1064
- package/ios/ReactNativeBlobUtilFileTransformer.h +23 -23
- package/ios/ReactNativeBlobUtilFileTransformer.mm +20 -20
- package/ios/ReactNativeBlobUtilNetwork.h +49 -49
- package/ios/ReactNativeBlobUtilNetwork.mm +159 -159
- package/ios/ReactNativeBlobUtilProgress.h +32 -32
- package/ios/ReactNativeBlobUtilProgress.mm +57 -57
- package/ios/ReactNativeBlobUtilReqBuilder.h +37 -37
- package/ios/ReactNativeBlobUtilReqBuilder.mm +292 -292
- package/ios/ReactNativeBlobUtilRequest.h +48 -48
- package/ios/ReactNativeBlobUtilRequest.mm +584 -584
- package/ios.js +61 -61
- package/json-stream.js +42 -42
- package/lib/oboe-browser.js +2703 -2703
- package/mediacollection.js +36 -36
- package/package.json +68 -68
- package/polyfill/Blob.js +362 -362
- package/polyfill/Event.js +11 -11
- package/polyfill/EventTarget.js +78 -78
- package/polyfill/Fetch.js +219 -219
- package/polyfill/File.js +27 -27
- package/polyfill/FileReader.js +91 -91
- package/polyfill/ProgressEvent.js +32 -32
- package/polyfill/XMLHttpRequest.js +466 -466
- package/polyfill/XMLHttpRequestEventTarget.js +126 -126
- package/polyfill/index.js +11 -11
- package/react-native-blob-util.podspec +48 -48
- package/types.js +69 -69
- package/utils/log.js +40 -40
- package/utils/unicode.js +7 -7
- package/utils/uri.js +33 -33
- package/utils/uuid.js +4 -4
- package/windows/ExperimentalFeatures.props +33 -33
- package/windows/README.md +2 -2
- package/windows/ReactNativeBlobUtil/PropertySheet.props +15 -15
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.cpp +1836 -1836
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.def +3 -3
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.h +372 -372
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj +139 -139
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj.filters +43 -43
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.cpp +20 -20
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.h +24 -24
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.idl +9 -9
- package/windows/ReactNativeBlobUtil/codegen/.clang-format +1 -1
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsDataTypes.g.h +42 -42
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsSpec.g.h +353 -353
- package/windows/ReactNativeBlobUtil/packages.config +3 -3
- package/windows/ReactNativeBlobUtil/packages.lock.json +59 -59
- package/windows/ReactNativeBlobUtil/pch.cpp +1 -1
- package/windows/ReactNativeBlobUtil/pch.h +30 -30
- package/windows/ReactNativeBlobUtil/resource.h +5 -5
- package/windows/ReactNativeBlobUtil/targetver.h +8 -8
- package/windows/ReactNativeBlobUtil.sln +43 -43
|
@@ -1,1014 +1,1014 @@
|
|
|
1
|
-
package com.ReactNativeBlobUtil;
|
|
2
|
-
|
|
3
|
-
import android.app.DownloadManager;
|
|
4
|
-
import android.content.BroadcastReceiver;
|
|
5
|
-
import android.content.Context;
|
|
6
|
-
import android.content.Intent;
|
|
7
|
-
import android.content.IntentFilter;
|
|
8
|
-
import android.database.Cursor;
|
|
9
|
-
import android.net.ConnectivityManager;
|
|
10
|
-
import android.net.Network;
|
|
11
|
-
import android.net.NetworkCapabilities;
|
|
12
|
-
import android.net.NetworkInfo;
|
|
13
|
-
import android.net.Uri;
|
|
14
|
-
import android.os.Build;
|
|
15
|
-
import android.os.Bundle;
|
|
16
|
-
import android.os.Environment;
|
|
17
|
-
import android.os.Handler;
|
|
18
|
-
import android.os.Message;
|
|
19
|
-
import android.util.Base64;
|
|
20
|
-
import android.webkit.CookieManager;
|
|
21
|
-
|
|
22
|
-
import androidx.annotation.NonNull;
|
|
23
|
-
|
|
24
|
-
import com.ReactNativeBlobUtil.Response.ReactNativeBlobUtilDefaultResp;
|
|
25
|
-
import com.ReactNativeBlobUtil.Response.ReactNativeBlobUtilFileResp;
|
|
26
|
-
import com.ReactNativeBlobUtil.Utils.Tls12SocketFactory;
|
|
27
|
-
import com.ReactNativeBlobUtil.ReactNativeBlobUtilFS;
|
|
28
|
-
import com.facebook.common.logging.FLog;
|
|
29
|
-
import com.facebook.react.bridge.Arguments;
|
|
30
|
-
import com.facebook.react.bridge.Callback;
|
|
31
|
-
import com.facebook.react.bridge.ReadableArray;
|
|
32
|
-
import com.facebook.react.bridge.ReadableMap;
|
|
33
|
-
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
|
34
|
-
import com.facebook.react.bridge.WritableArray;
|
|
35
|
-
import com.facebook.react.bridge.WritableMap;
|
|
36
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
37
|
-
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
38
|
-
|
|
39
|
-
import java.io.File;
|
|
40
|
-
import java.io.FileOutputStream;
|
|
41
|
-
import java.io.IOException;
|
|
42
|
-
import java.io.InputStream;
|
|
43
|
-
import java.net.MalformedURLException;
|
|
44
|
-
import java.net.Proxy;
|
|
45
|
-
import java.net.SocketException;
|
|
46
|
-
import java.net.SocketTimeoutException;
|
|
47
|
-
import java.net.URL;
|
|
48
|
-
import java.nio.ByteBuffer;
|
|
49
|
-
import java.nio.charset.CharacterCodingException;
|
|
50
|
-
import java.nio.charset.Charset;
|
|
51
|
-
import java.nio.charset.CharsetDecoder;
|
|
52
|
-
import java.util.ArrayList;
|
|
53
|
-
import java.util.HashMap;
|
|
54
|
-
|
|
55
|
-
import java.util.UUID;
|
|
56
|
-
|
|
57
|
-
import java.util.List;
|
|
58
|
-
import java.util.Locale;
|
|
59
|
-
import java.util.concurrent.Executors;
|
|
60
|
-
import java.util.concurrent.Future;
|
|
61
|
-
import java.util.concurrent.ScheduledExecutorService;
|
|
62
|
-
import java.util.concurrent.TimeUnit;
|
|
63
|
-
|
|
64
|
-
import javax.net.ssl.SSLContext;
|
|
65
|
-
|
|
66
|
-
import okhttp3.Call;
|
|
67
|
-
import okhttp3.ConnectionPool;
|
|
68
|
-
import okhttp3.ConnectionSpec;
|
|
69
|
-
import okhttp3.Headers;
|
|
70
|
-
import okhttp3.Interceptor;
|
|
71
|
-
import okhttp3.MediaType;
|
|
72
|
-
import okhttp3.OkHttpClient;
|
|
73
|
-
import okhttp3.Request;
|
|
74
|
-
import okhttp3.RequestBody;
|
|
75
|
-
import okhttp3.Response;
|
|
76
|
-
import okhttp3.ResponseBody;
|
|
77
|
-
import okhttp3.TlsVersion;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnable {
|
|
81
|
-
|
|
82
|
-
enum RequestType {
|
|
83
|
-
Form,
|
|
84
|
-
SingleFile,
|
|
85
|
-
AsIs,
|
|
86
|
-
WithoutBody,
|
|
87
|
-
Others
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
enum ResponseType {
|
|
91
|
-
KeepInMemory,
|
|
92
|
-
FileStorage
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
enum ResponseFormat {
|
|
96
|
-
Auto,
|
|
97
|
-
UTF8,
|
|
98
|
-
BASE64
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private boolean shouldTransformFile() {
|
|
102
|
-
return this.options.transformFile &&
|
|
103
|
-
// Can only process if it's written to a file
|
|
104
|
-
(this.options.fileCache || this.options.path != null);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
public static HashMap<String, Call> taskTable = new HashMap<>();
|
|
108
|
-
public static HashMap<String, Long> androidDownloadManagerTaskTable = new HashMap<>();
|
|
109
|
-
static HashMap<String, ReactNativeBlobUtilProgressConfig> progressReport = new HashMap<>();
|
|
110
|
-
static HashMap<String, ReactNativeBlobUtilProgressConfig> uploadProgressReport = new HashMap<>();
|
|
111
|
-
static ConnectionPool pool = new ConnectionPool();
|
|
112
|
-
|
|
113
|
-
ReactNativeBlobUtilConfig options;
|
|
114
|
-
String taskId;
|
|
115
|
-
String method;
|
|
116
|
-
String url;
|
|
117
|
-
String rawRequestBody;
|
|
118
|
-
String destPath;
|
|
119
|
-
String customPath;
|
|
120
|
-
ReadableArray rawRequestBodyArray;
|
|
121
|
-
ReadableMap headers;
|
|
122
|
-
Callback callback;
|
|
123
|
-
long contentLength;
|
|
124
|
-
long downloadManagerId;
|
|
125
|
-
ReactNativeBlobUtilBody requestBody;
|
|
126
|
-
RequestType requestType;
|
|
127
|
-
ResponseType responseType;
|
|
128
|
-
ResponseFormat responseFormat = ResponseFormat.Auto;
|
|
129
|
-
WritableMap respInfo;
|
|
130
|
-
boolean timeout = false;
|
|
131
|
-
ArrayList<String> redirects = new ArrayList<>();
|
|
132
|
-
OkHttpClient client;
|
|
133
|
-
boolean callbackfired;
|
|
134
|
-
|
|
135
|
-
public ReactNativeBlobUtilReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, OkHttpClient client, final Callback callback) {
|
|
136
|
-
this.method = method.toUpperCase(Locale.ROOT);
|
|
137
|
-
this.options = new ReactNativeBlobUtilConfig(options);
|
|
138
|
-
this.taskId = taskId;
|
|
139
|
-
this.url = url;
|
|
140
|
-
this.headers = headers;
|
|
141
|
-
this.callback = callback;
|
|
142
|
-
this.rawRequestBody = body;
|
|
143
|
-
this.rawRequestBodyArray = arrayBody;
|
|
144
|
-
this.client = client;
|
|
145
|
-
this.callbackfired = false;
|
|
146
|
-
|
|
147
|
-
// If transformFile is specified, we first want to get the response back in memory so we can
|
|
148
|
-
// encrypt it wholesale and at that point, write it into the file storage.
|
|
149
|
-
if((this.options.fileCache || this.options.path != null) && !this.shouldTransformFile())
|
|
150
|
-
responseType = ResponseType.FileStorage;
|
|
151
|
-
else
|
|
152
|
-
responseType = ResponseType.KeepInMemory;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (body != null)
|
|
156
|
-
requestType = RequestType.SingleFile;
|
|
157
|
-
else if (arrayBody != null)
|
|
158
|
-
requestType = RequestType.Form;
|
|
159
|
-
else
|
|
160
|
-
requestType = RequestType.WithoutBody;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
public static void cancelTask(String taskId) {
|
|
164
|
-
Call call = taskTable.get(taskId);
|
|
165
|
-
if (call != null) {
|
|
166
|
-
call.cancel();
|
|
167
|
-
taskTable.remove(taskId);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (androidDownloadManagerTaskTable.containsKey(taskId)) {
|
|
171
|
-
long downloadManagerIdForTaskId = androidDownloadManagerTaskTable.get(taskId).longValue();
|
|
172
|
-
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
173
|
-
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
174
|
-
dm.remove(downloadManagerIdForTaskId);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private void invoke_callback(Object... args) {
|
|
179
|
-
if (this.callbackfired) return;
|
|
180
|
-
this.callback.invoke(args);
|
|
181
|
-
this.callbackfired = true;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
private final int QUERY = 1314;
|
|
185
|
-
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
|
186
|
-
private Future<?> future;
|
|
187
|
-
private Handler mHandler = new Handler(new Handler.Callback() {
|
|
188
|
-
public boolean handleMessage(Message msg) {
|
|
189
|
-
switch (msg.what) {
|
|
190
|
-
|
|
191
|
-
case QUERY:
|
|
192
|
-
|
|
193
|
-
Bundle data = msg.getData();
|
|
194
|
-
long id = data.getLong("downloadManagerId");
|
|
195
|
-
if (id == downloadManagerId) {
|
|
196
|
-
|
|
197
|
-
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
198
|
-
|
|
199
|
-
DownloadManager downloadManager = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
200
|
-
|
|
201
|
-
DownloadManager.Query query = new DownloadManager.Query();
|
|
202
|
-
query.setFilterById(downloadManagerId);
|
|
203
|
-
|
|
204
|
-
Cursor cursor = downloadManager.query(query);
|
|
205
|
-
|
|
206
|
-
if (cursor != null && cursor.moveToFirst()) {
|
|
207
|
-
|
|
208
|
-
long written = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
|
|
209
|
-
|
|
210
|
-
long total = cursor.getLong(cursor.getColumnIndex(
|
|
211
|
-
DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
|
|
212
|
-
cursor.close();
|
|
213
|
-
|
|
214
|
-
ReactNativeBlobUtilProgressConfig reportConfig = getReportProgress(taskId);
|
|
215
|
-
float progress = (total > 0) ? written / total : 0;
|
|
216
|
-
|
|
217
|
-
if (reportConfig != null && reportConfig.shouldReport(progress /* progress */)) {
|
|
218
|
-
WritableMap args = Arguments.createMap();
|
|
219
|
-
args.putString("taskId", String.valueOf(taskId));
|
|
220
|
-
args.putString("written", String.valueOf(written));
|
|
221
|
-
args.putString("total", String.valueOf(total));
|
|
222
|
-
args.putString("chunk", "");
|
|
223
|
-
ReactNativeBlobUtilImpl.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
224
|
-
.emit(ReactNativeBlobUtilConst.EVENT_PROGRESS, args);
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (total == written) {
|
|
229
|
-
future.cancel(true);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
@Override
|
|
239
|
-
public void run() {
|
|
240
|
-
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
241
|
-
// use download manager instead of default HTTP implementation
|
|
242
|
-
if (options.addAndroidDownloads != null && options.addAndroidDownloads.hasKey("useDownloadManager")) {
|
|
243
|
-
|
|
244
|
-
if (options.addAndroidDownloads.getBoolean("useDownloadManager")) {
|
|
245
|
-
Uri uri = Uri.parse(url);
|
|
246
|
-
DownloadManager.Request req = new DownloadManager.Request(uri);
|
|
247
|
-
if (options.addAndroidDownloads.hasKey("notification") && options.addAndroidDownloads.getBoolean("notification")) {
|
|
248
|
-
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
|
249
|
-
} else {
|
|
250
|
-
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
|
|
251
|
-
}
|
|
252
|
-
if (options.addAndroidDownloads.hasKey("title")) {
|
|
253
|
-
req.setTitle(options.addAndroidDownloads.getString("title"));
|
|
254
|
-
}
|
|
255
|
-
if (options.addAndroidDownloads.hasKey("description")) {
|
|
256
|
-
req.setDescription(options.addAndroidDownloads.getString("description"));
|
|
257
|
-
}
|
|
258
|
-
if (options.addAndroidDownloads.hasKey("path")) {
|
|
259
|
-
String path = options.addAndroidDownloads.getString("path");
|
|
260
|
-
File f = new File(path);
|
|
261
|
-
File dir = f.getParentFile();
|
|
262
|
-
|
|
263
|
-
if (!f.exists()) {
|
|
264
|
-
if (dir != null && !dir.exists()) {
|
|
265
|
-
if (!dir.mkdirs() && !dir.exists()) {
|
|
266
|
-
invoke_callback( "Failed to create parent directory of '" + path + "'", null, null);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
req.setDestinationUri(Uri.parse("file://" + path));
|
|
272
|
-
|
|
273
|
-
customPath = path;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (options.addAndroidDownloads.hasKey("storeLocal") && options.addAndroidDownloads.getBoolean("storeLocal")) {
|
|
278
|
-
String path = (String) ReactNativeBlobUtilFS.getSystemfolders(ReactNativeBlobUtilImpl.RCTContext).get("DownloadDir");
|
|
279
|
-
path = path + UUID.randomUUID().toString();
|
|
280
|
-
|
|
281
|
-
File f = new File(path);
|
|
282
|
-
File dir = f.getParentFile();
|
|
283
|
-
if (!f.exists()) {
|
|
284
|
-
if (dir != null && !dir.exists()) {
|
|
285
|
-
if (!dir.mkdirs() && !dir.exists()) {
|
|
286
|
-
invoke_callback( "Failed to create parent directory of '" + path + "'", null, null);
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
req.setDestinationUri(Uri.parse("file://" + path));
|
|
292
|
-
customPath = path;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (options.addAndroidDownloads.hasKey("mime")) {
|
|
296
|
-
req.setMimeType(options.addAndroidDownloads.getString("mime"));
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (options.addAndroidDownloads.hasKey("mediaScannable") && options.addAndroidDownloads.getBoolean("mediaScannable")) {
|
|
300
|
-
req.allowScanningByMediaScanner();
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")) {
|
|
304
|
-
String t = options.addAndroidDownloads.getString("title");
|
|
305
|
-
if(t == null || t.isEmpty())
|
|
306
|
-
t = UUID.randomUUID().toString();
|
|
307
|
-
if(this.options.appendExt != null && !this.options.appendExt.isEmpty())
|
|
308
|
-
t += "." + this.options.appendExt;
|
|
309
|
-
|
|
310
|
-
req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, t);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// set headers
|
|
314
|
-
ReadableMapKeySetIterator it = headers.keySetIterator();
|
|
315
|
-
while (it.hasNextKey()) {
|
|
316
|
-
String key = it.nextKey();
|
|
317
|
-
req.addRequestHeader(key, headers.getString(key));
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Attempt to add cookie, if it exists
|
|
321
|
-
URL urlObj;
|
|
322
|
-
try {
|
|
323
|
-
urlObj = new URL(url);
|
|
324
|
-
String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
|
|
325
|
-
String cookie = CookieManager.getInstance().getCookie(baseUrl);
|
|
326
|
-
req.addRequestHeader("Cookie", cookie);
|
|
327
|
-
} catch (MalformedURLException e) {
|
|
328
|
-
e.printStackTrace();
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
332
|
-
downloadManagerId = dm.enqueue(req);
|
|
333
|
-
androidDownloadManagerTaskTable.put(taskId, Long.valueOf(downloadManagerId));
|
|
334
|
-
if(Build.VERSION.SDK_INT >= 34 ){
|
|
335
|
-
appCtx.registerReceiver(this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), Context.RECEIVER_EXPORTED);
|
|
336
|
-
}else{
|
|
337
|
-
appCtx.registerReceiver(this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
|
338
|
-
}
|
|
339
|
-
future = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
|
|
340
|
-
@Override
|
|
341
|
-
public void run() {
|
|
342
|
-
Message msg = mHandler.obtainMessage();
|
|
343
|
-
Bundle data = new Bundle();
|
|
344
|
-
data.putLong("downloadManagerId", downloadManagerId);
|
|
345
|
-
msg.setData(data);
|
|
346
|
-
msg.what = QUERY;
|
|
347
|
-
mHandler.sendMessage(msg);
|
|
348
|
-
}
|
|
349
|
-
}, 0, 100, TimeUnit.MILLISECONDS);
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// find cached result if `key` property exists
|
|
356
|
-
String cacheKey = this.taskId;
|
|
357
|
-
String ext = (this.options.appendExt == null || this.options.appendExt.isEmpty()) ? "" : "." + this.options.appendExt;
|
|
358
|
-
|
|
359
|
-
if (this.options.key != null) {
|
|
360
|
-
cacheKey = ReactNativeBlobUtilUtils.getMD5(this.options.key);
|
|
361
|
-
if (cacheKey == null) {
|
|
362
|
-
cacheKey = this.taskId;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
File file = new File(ReactNativeBlobUtilFS.getTmpPath(cacheKey) + ext);
|
|
366
|
-
|
|
367
|
-
if (file.exists()) {
|
|
368
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, file.getAbsolutePath());
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (this.options.path != null)
|
|
374
|
-
this.destPath = this.options.path;
|
|
375
|
-
else if (this.options.fileCache)
|
|
376
|
-
this.destPath = ReactNativeBlobUtilFS.getTmpPath(cacheKey) + ext;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
OkHttpClient.Builder clientBuilder;
|
|
380
|
-
|
|
381
|
-
try {
|
|
382
|
-
// use trusty SSL socket
|
|
383
|
-
if (this.options.trusty) {
|
|
384
|
-
clientBuilder = ReactNativeBlobUtilUtils.getUnsafeOkHttpClient(client);
|
|
385
|
-
} else {
|
|
386
|
-
clientBuilder = client.newBuilder();
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// wifi only, need ACCESS_NETWORK_STATE permission
|
|
390
|
-
// and API level >= 21
|
|
391
|
-
if (this.options.wifiOnly) {
|
|
392
|
-
|
|
393
|
-
boolean found = false;
|
|
394
|
-
|
|
395
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
396
|
-
ConnectivityManager connectivityManager = (ConnectivityManager) ReactNativeBlobUtilImpl.RCTContext.getSystemService(ReactNativeBlobUtilImpl.RCTContext.CONNECTIVITY_SERVICE);
|
|
397
|
-
Network[] networks = connectivityManager.getAllNetworks();
|
|
398
|
-
|
|
399
|
-
for (Network network : networks) {
|
|
400
|
-
|
|
401
|
-
NetworkInfo netInfo = connectivityManager.getNetworkInfo(network);
|
|
402
|
-
NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(network);
|
|
403
|
-
|
|
404
|
-
if (caps == null || netInfo == null) {
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
if (!netInfo.isConnected()) {
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
|
413
|
-
clientBuilder.proxy(Proxy.NO_PROXY);
|
|
414
|
-
clientBuilder.socketFactory(network.getSocketFactory());
|
|
415
|
-
found = true;
|
|
416
|
-
break;
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (!found) {
|
|
422
|
-
invoke_callback("No available WiFi connections.", null, null);
|
|
423
|
-
releaseTaskResource();
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
} else {
|
|
427
|
-
ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil: wifiOnly was set, but SDK < 21. wifiOnly was ignored.");
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
final Request.Builder builder = new Request.Builder();
|
|
432
|
-
try {
|
|
433
|
-
builder.url(new URL(url));
|
|
434
|
-
} catch (MalformedURLException e) {
|
|
435
|
-
e.printStackTrace();
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
HashMap<String, String> mheaders = new HashMap<>();
|
|
439
|
-
// set headers
|
|
440
|
-
if (headers != null) {
|
|
441
|
-
ReadableMapKeySetIterator it = headers.keySetIterator();
|
|
442
|
-
while (it.hasNextKey()) {
|
|
443
|
-
String key = it.nextKey();
|
|
444
|
-
String value = headers.getString(key);
|
|
445
|
-
if (key.equalsIgnoreCase("RNFB-Response")) {
|
|
446
|
-
if (value.equalsIgnoreCase("base64"))
|
|
447
|
-
responseFormat = ResponseFormat.BASE64;
|
|
448
|
-
else if (value.equalsIgnoreCase("utf8"))
|
|
449
|
-
responseFormat = ResponseFormat.UTF8;
|
|
450
|
-
} else {
|
|
451
|
-
builder.header(key.toLowerCase(Locale.ROOT), value);
|
|
452
|
-
mheaders.put(key.toLowerCase(Locale.ROOT), value);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
|
|
458
|
-
String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase(Locale.ROOT);
|
|
459
|
-
|
|
460
|
-
if (rawRequestBodyArray != null) {
|
|
461
|
-
requestType = RequestType.Form;
|
|
462
|
-
} else if (cType == null || cType.isEmpty()) {
|
|
463
|
-
if (!cType.equalsIgnoreCase("")) {
|
|
464
|
-
builder.header("Content-Type", "application/octet-stream");
|
|
465
|
-
}
|
|
466
|
-
requestType = RequestType.SingleFile;
|
|
467
|
-
}
|
|
468
|
-
if (rawRequestBody != null) {
|
|
469
|
-
if (rawRequestBody.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX)
|
|
470
|
-
|| rawRequestBody.startsWith(ReactNativeBlobUtilConst.CONTENT_PREFIX)) {
|
|
471
|
-
requestType = RequestType.SingleFile;
|
|
472
|
-
} else if (cType.toLowerCase(Locale.ROOT).contains(";base64") || cType.toLowerCase(Locale.ROOT).startsWith("application/octet")) {
|
|
473
|
-
cType = cType.replace(";base64", "").replace(";BASE64", "");
|
|
474
|
-
if (mheaders.containsKey("content-type"))
|
|
475
|
-
mheaders.put("content-type", cType);
|
|
476
|
-
if (mheaders.containsKey("Content-Type"))
|
|
477
|
-
mheaders.put("Content-Type", cType);
|
|
478
|
-
requestType = RequestType.SingleFile;
|
|
479
|
-
} else {
|
|
480
|
-
requestType = RequestType.AsIs;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
} else {
|
|
484
|
-
requestType = RequestType.WithoutBody;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
boolean isChunkedRequest = getHeaderIgnoreCases(mheaders, "Transfer-Encoding").equalsIgnoreCase("chunked");
|
|
488
|
-
|
|
489
|
-
// set request body
|
|
490
|
-
switch (requestType) {
|
|
491
|
-
case SingleFile:
|
|
492
|
-
requestBody = new ReactNativeBlobUtilBody(taskId)
|
|
493
|
-
.chunkedEncoding(isChunkedRequest)
|
|
494
|
-
.setRequestType(requestType)
|
|
495
|
-
.setBody(rawRequestBody)
|
|
496
|
-
.setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
|
|
497
|
-
builder.method(method, requestBody);
|
|
498
|
-
break;
|
|
499
|
-
case AsIs:
|
|
500
|
-
requestBody = new ReactNativeBlobUtilBody(taskId)
|
|
501
|
-
.chunkedEncoding(isChunkedRequest)
|
|
502
|
-
.setRequestType(requestType)
|
|
503
|
-
.setBody(rawRequestBody)
|
|
504
|
-
.setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
|
|
505
|
-
builder.method(method, requestBody);
|
|
506
|
-
break;
|
|
507
|
-
case Form:
|
|
508
|
-
String boundary = "ReactNativeBlobUtil-" + taskId;
|
|
509
|
-
requestBody = new ReactNativeBlobUtilBody(taskId)
|
|
510
|
-
.chunkedEncoding(isChunkedRequest)
|
|
511
|
-
.setRequestType(requestType)
|
|
512
|
-
.setBody(rawRequestBodyArray)
|
|
513
|
-
.setMIME(MediaType.parse("multipart/form-data; boundary=" + boundary));
|
|
514
|
-
builder.method(method, requestBody);
|
|
515
|
-
break;
|
|
516
|
-
|
|
517
|
-
case WithoutBody:
|
|
518
|
-
if (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
|
|
519
|
-
builder.method(method, RequestBody.create(null, new byte[0]));
|
|
520
|
-
} else
|
|
521
|
-
builder.method(method, null);
|
|
522
|
-
break;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// #156 fix cookie issue
|
|
526
|
-
final Request req = builder.build();
|
|
527
|
-
clientBuilder.addNetworkInterceptor(new Interceptor() {
|
|
528
|
-
@NonNull
|
|
529
|
-
@Override
|
|
530
|
-
public Response intercept(@NonNull Chain chain) throws IOException {
|
|
531
|
-
redirects.add(chain.request().url().toString());
|
|
532
|
-
return chain.proceed(chain.request());
|
|
533
|
-
}
|
|
534
|
-
});
|
|
535
|
-
// Add request interceptor for upload progress event
|
|
536
|
-
clientBuilder.addInterceptor(new Interceptor() {
|
|
537
|
-
@NonNull
|
|
538
|
-
@Override
|
|
539
|
-
public Response intercept(@NonNull Chain chain) throws IOException {
|
|
540
|
-
Response originalResponse = null;
|
|
541
|
-
try {
|
|
542
|
-
originalResponse = chain.proceed(req);
|
|
543
|
-
ResponseBody extended;
|
|
544
|
-
switch (responseType) {
|
|
545
|
-
case KeepInMemory:
|
|
546
|
-
extended = new ReactNativeBlobUtilDefaultResp(
|
|
547
|
-
ReactNativeBlobUtilImpl.RCTContext,
|
|
548
|
-
taskId,
|
|
549
|
-
originalResponse.body(),
|
|
550
|
-
options.increment);
|
|
551
|
-
break;
|
|
552
|
-
case FileStorage:
|
|
553
|
-
extended = new ReactNativeBlobUtilFileResp(
|
|
554
|
-
ReactNativeBlobUtilImpl.RCTContext,
|
|
555
|
-
taskId,
|
|
556
|
-
originalResponse.body(),
|
|
557
|
-
destPath,
|
|
558
|
-
options.overwrite);
|
|
559
|
-
break;
|
|
560
|
-
default:
|
|
561
|
-
extended = new ReactNativeBlobUtilDefaultResp(
|
|
562
|
-
ReactNativeBlobUtilImpl.RCTContext,
|
|
563
|
-
taskId,
|
|
564
|
-
originalResponse.body(),
|
|
565
|
-
options.increment);
|
|
566
|
-
break;
|
|
567
|
-
}
|
|
568
|
-
return originalResponse.newBuilder().body(extended).build();
|
|
569
|
-
} catch (SocketException e) {
|
|
570
|
-
timeout = true;
|
|
571
|
-
if (originalResponse != null) {
|
|
572
|
-
originalResponse.close();
|
|
573
|
-
}
|
|
574
|
-
} catch (SocketTimeoutException e) {
|
|
575
|
-
timeout = true;
|
|
576
|
-
if (originalResponse != null) {
|
|
577
|
-
originalResponse.close();
|
|
578
|
-
}
|
|
579
|
-
//ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil error when sending request : " + e.getLocalizedMessage());
|
|
580
|
-
} catch (Exception ex) {
|
|
581
|
-
if (originalResponse != null) {
|
|
582
|
-
originalResponse.close();
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
return chain.proceed(chain.request());
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if (options.timeout >= 0) {
|
|
592
|
-
clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
|
|
593
|
-
clientBuilder.readTimeout(options.timeout, TimeUnit.MILLISECONDS);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
clientBuilder.connectionPool(pool);
|
|
597
|
-
clientBuilder.retryOnConnectionFailure(false);
|
|
598
|
-
clientBuilder.followRedirects(options.followRedirect);
|
|
599
|
-
clientBuilder.followSslRedirects(options.followRedirect);
|
|
600
|
-
clientBuilder.retryOnConnectionFailure(true);
|
|
601
|
-
|
|
602
|
-
OkHttpClient client = enableTls12OnPreLollipop(clientBuilder).build();
|
|
603
|
-
|
|
604
|
-
Call call = client.newCall(req);
|
|
605
|
-
taskTable.put(taskId, call);
|
|
606
|
-
call.enqueue(new okhttp3.Callback() {
|
|
607
|
-
|
|
608
|
-
@Override
|
|
609
|
-
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
610
|
-
cancelTask(taskId);
|
|
611
|
-
if (respInfo == null) {
|
|
612
|
-
respInfo = Arguments.createMap();
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// check if this error caused by socket timeout
|
|
616
|
-
if (e.getClass().equals(SocketTimeoutException.class)) {
|
|
617
|
-
respInfo.putBoolean("timeout", true);
|
|
618
|
-
invoke_callback("The request timed out.", null, null);
|
|
619
|
-
} else
|
|
620
|
-
invoke_callback(e.getLocalizedMessage(), null, null);
|
|
621
|
-
releaseTaskResource();
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
@Override
|
|
625
|
-
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
|
|
626
|
-
ReadableMap notifyConfig = options.addAndroidDownloads;
|
|
627
|
-
// Download manager settings
|
|
628
|
-
if (notifyConfig != null) {
|
|
629
|
-
String title = "", desc = "", mime = "text/plain";
|
|
630
|
-
boolean scannable = false, notification = false;
|
|
631
|
-
if (notifyConfig.hasKey("title"))
|
|
632
|
-
title = options.addAndroidDownloads.getString("title");
|
|
633
|
-
if (notifyConfig.hasKey("description"))
|
|
634
|
-
desc = notifyConfig.getString("description");
|
|
635
|
-
if (notifyConfig.hasKey("mime"))
|
|
636
|
-
mime = notifyConfig.getString("mime");
|
|
637
|
-
if (notifyConfig.hasKey("mediaScannable"))
|
|
638
|
-
scannable = notifyConfig.getBoolean("mediaScannable");
|
|
639
|
-
if (notifyConfig.hasKey("notification"))
|
|
640
|
-
notification = notifyConfig.getBoolean("notification");
|
|
641
|
-
DownloadManager dm = (DownloadManager) ReactNativeBlobUtilImpl.RCTContext.getSystemService(ReactNativeBlobUtilImpl.RCTContext.DOWNLOAD_SERVICE);
|
|
642
|
-
dm.addCompletedDownload(title, desc, scannable, mime, destPath, contentLength, notification);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
done(response);
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
} catch (Exception error) {
|
|
651
|
-
error.printStackTrace();
|
|
652
|
-
releaseTaskResource();
|
|
653
|
-
invoke_callback("ReactNativeBlobUtil request error: " + error.getMessage() + error.getCause());
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* Remove cached information of the HTTP task
|
|
659
|
-
*/
|
|
660
|
-
private void releaseTaskResource() {
|
|
661
|
-
if (taskTable.containsKey(taskId))
|
|
662
|
-
taskTable.remove(taskId);
|
|
663
|
-
if (androidDownloadManagerTaskTable.containsKey(taskId))
|
|
664
|
-
androidDownloadManagerTaskTable.remove(taskId);
|
|
665
|
-
if (uploadProgressReport.containsKey(taskId))
|
|
666
|
-
uploadProgressReport.remove(taskId);
|
|
667
|
-
if (progressReport.containsKey(taskId))
|
|
668
|
-
progressReport.remove(taskId);
|
|
669
|
-
if (requestBody != null)
|
|
670
|
-
requestBody.clearRequestBody();
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* Send response data back to javascript context.
|
|
675
|
-
*
|
|
676
|
-
* @param resp OkHttp response object
|
|
677
|
-
*/
|
|
678
|
-
private void done(Response resp) {
|
|
679
|
-
boolean isBlobResp = isBlobResponse(resp);
|
|
680
|
-
WritableMap respmap = getResponseInfo(resp,isBlobResp);
|
|
681
|
-
emitStateEvent(respmap.copy());
|
|
682
|
-
|
|
683
|
-
emitStateEvent(getResponseInfo(resp, isBlobResp));
|
|
684
|
-
switch (responseType) {
|
|
685
|
-
case KeepInMemory:
|
|
686
|
-
try {
|
|
687
|
-
// For XMLHttpRequest, automatic response data storing strategy, when response
|
|
688
|
-
// data is considered as binary data, write it to file system
|
|
689
|
-
if (isBlobResp && options.auto) {
|
|
690
|
-
String dest = ReactNativeBlobUtilFS.getTmpPath(taskId);
|
|
691
|
-
InputStream ins = resp.body().byteStream();
|
|
692
|
-
FileOutputStream os = new FileOutputStream(new File(dest));
|
|
693
|
-
int read;
|
|
694
|
-
byte[] buffer = new byte[10240];
|
|
695
|
-
while ((read = ins.read(buffer)) != -1) {
|
|
696
|
-
os.write(buffer, 0, read);
|
|
697
|
-
}
|
|
698
|
-
ins.close();
|
|
699
|
-
os.flush();
|
|
700
|
-
os.close();
|
|
701
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, dest, respmap.copy());
|
|
702
|
-
}
|
|
703
|
-
// response data directly pass to JS context as string.
|
|
704
|
-
else {
|
|
705
|
-
byte[] b = resp.body().bytes();
|
|
706
|
-
// If process file option is turned on, we first keep response in memory and then stream that content
|
|
707
|
-
// after processing
|
|
708
|
-
if (this.shouldTransformFile()) {
|
|
709
|
-
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
|
|
710
|
-
throw new IllegalStateException("Write file with transform was specified but the shared file transformer is not set");
|
|
711
|
-
}
|
|
712
|
-
this.destPath = this.destPath.replace("?append=true", "");
|
|
713
|
-
File file = new File(this.destPath);
|
|
714
|
-
if (!file.exists()) {
|
|
715
|
-
file.createNewFile();
|
|
716
|
-
}
|
|
717
|
-
try (FileOutputStream fos = new FileOutputStream(file)) {
|
|
718
|
-
fos.write(ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(b));
|
|
719
|
-
} catch (Exception e) {
|
|
720
|
-
invoke_callback("Error from file transformer:" + e.getLocalizedMessage(), respmap.copy());
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, this.destPath, respmap.copy());
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
if (responseFormat == ResponseFormat.BASE64) {
|
|
727
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP), respmap.copy());
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
try {
|
|
731
|
-
// Attempt to decode the incoming response data to determine whether it contains a valid UTF8 string
|
|
732
|
-
Charset charSet = Charset.forName("UTF-8");
|
|
733
|
-
CharsetDecoder decoder = charSet.newDecoder();
|
|
734
|
-
decoder.decode(ByteBuffer.wrap(b));
|
|
735
|
-
// If the data contains invalid characters the following lines will be skipped.
|
|
736
|
-
String utf8 = new String(b, charSet);
|
|
737
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_UTF8, utf8);
|
|
738
|
-
}
|
|
739
|
-
// This usually means the data contains invalid unicode characters but still valid data,
|
|
740
|
-
// it's binary data, so send it as a normal string
|
|
741
|
-
catch (CharacterCodingException ignored) {
|
|
742
|
-
if (responseFormat == ResponseFormat.UTF8) {
|
|
743
|
-
String utf8 = new String(b);
|
|
744
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_UTF8, utf8, respmap.copy());
|
|
745
|
-
} else {
|
|
746
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP), respmap.copy());
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
} catch (IOException e) {
|
|
751
|
-
invoke_callback("ReactNativeBlobUtil failed to encode response data to BASE64 string.", respmap.copy());
|
|
752
|
-
}
|
|
753
|
-
break;
|
|
754
|
-
case FileStorage:
|
|
755
|
-
ResponseBody responseBody = resp.body();
|
|
756
|
-
|
|
757
|
-
try {
|
|
758
|
-
// In order to write response data to `destPath` we have to invoke this method.
|
|
759
|
-
// It uses customized response body which is able to report download progress
|
|
760
|
-
// and write response data to destination path.
|
|
761
|
-
responseBody.bytes();
|
|
762
|
-
} catch (Exception ignored) {
|
|
763
|
-
// ignored.printStackTrace();
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
ReactNativeBlobUtilFileResp ReactNativeBlobUtilFileResp;
|
|
767
|
-
|
|
768
|
-
try {
|
|
769
|
-
ReactNativeBlobUtilFileResp = (ReactNativeBlobUtilFileResp) responseBody;
|
|
770
|
-
} catch (ClassCastException ex) {
|
|
771
|
-
// unexpected response type
|
|
772
|
-
if (responseBody != null) {
|
|
773
|
-
String responseBodyString = null;
|
|
774
|
-
try {
|
|
775
|
-
boolean isBufferDataExists = responseBody.source().buffer().size() > 0;
|
|
776
|
-
boolean isContentExists = responseBody.contentLength() > 0;
|
|
777
|
-
if (isBufferDataExists && isContentExists) {
|
|
778
|
-
responseBodyString = responseBody.string();
|
|
779
|
-
}
|
|
780
|
-
} catch (IOException exception) {
|
|
781
|
-
exception.printStackTrace();
|
|
782
|
-
}
|
|
783
|
-
invoke_callback("Unexpected FileStorage response file: " + responseBodyString, respmap.copy());
|
|
784
|
-
} else {
|
|
785
|
-
invoke_callback("Unexpected FileStorage response with no file.", respmap.copy());
|
|
786
|
-
}
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
if (ReactNativeBlobUtilFileResp != null && !ReactNativeBlobUtilFileResp.isDownloadComplete()) {
|
|
791
|
-
invoke_callback("Download interrupted.", respmap.copy());
|
|
792
|
-
} else {
|
|
793
|
-
this.destPath = this.destPath.replace("?append=true", "");
|
|
794
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, this.destPath, respmap.copy());
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
break;
|
|
798
|
-
default:
|
|
799
|
-
try {
|
|
800
|
-
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_UTF8, new String(resp.body().bytes(), "UTF-8"), respmap.copy());
|
|
801
|
-
} catch (IOException e) {
|
|
802
|
-
invoke_callback("ReactNativeBlobUtil failed to encode response data to UTF8 string.", respmap.copy());
|
|
803
|
-
}
|
|
804
|
-
break;
|
|
805
|
-
}
|
|
806
|
-
// if(!resp.isSuccessful())
|
|
807
|
-
resp.body().close();
|
|
808
|
-
releaseTaskResource();
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
/**
|
|
812
|
-
* Invoke this method to enable download progress reporting.
|
|
813
|
-
*
|
|
814
|
-
* @param taskId Task ID of the HTTP task.
|
|
815
|
-
* @return Task ID of the target task
|
|
816
|
-
*/
|
|
817
|
-
public static ReactNativeBlobUtilProgressConfig getReportProgress(String taskId) {
|
|
818
|
-
if (!progressReport.containsKey(taskId)) return null;
|
|
819
|
-
return progressReport.get(taskId);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Invoke this method to enable download progress reporting.
|
|
824
|
-
*
|
|
825
|
-
* @param taskId Task ID of the HTTP task.
|
|
826
|
-
* @return Task ID of the target task
|
|
827
|
-
*/
|
|
828
|
-
public static ReactNativeBlobUtilProgressConfig getReportUploadProgress(String taskId) {
|
|
829
|
-
if (!uploadProgressReport.containsKey(taskId)) return null;
|
|
830
|
-
return uploadProgressReport.get(taskId);
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* Create response information object, contains status code, headers, etc.
|
|
835
|
-
*
|
|
836
|
-
* @param resp Response object
|
|
837
|
-
* @param isBlobResp If the response is binary data
|
|
838
|
-
* @return Get RCT bridge object contains response information.
|
|
839
|
-
*/
|
|
840
|
-
private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
|
|
841
|
-
WritableMap info = Arguments.createMap();
|
|
842
|
-
info.putInt("status", resp.code());
|
|
843
|
-
info.putString("state", "2");
|
|
844
|
-
info.putString("taskId", this.taskId);
|
|
845
|
-
info.putBoolean("timeout", timeout);
|
|
846
|
-
WritableMap headers = Arguments.createMap();
|
|
847
|
-
for (int i = 0; i < resp.headers().size(); i++) {
|
|
848
|
-
headers.putString(resp.headers().name(i), resp.headers().value(i));
|
|
849
|
-
}
|
|
850
|
-
WritableArray redirectList = Arguments.createArray();
|
|
851
|
-
for (String r : redirects) {
|
|
852
|
-
redirectList.pushString(r);
|
|
853
|
-
}
|
|
854
|
-
info.putArray("redirects", redirectList);
|
|
855
|
-
info.putMap("headers", headers);
|
|
856
|
-
Headers h = resp.headers();
|
|
857
|
-
if (isBlobResp) {
|
|
858
|
-
info.putString("respType", "blob");
|
|
859
|
-
} else if (getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/")) {
|
|
860
|
-
info.putString("respType", "text");
|
|
861
|
-
} else if (getHeaderIgnoreCases(h, "content-type").contains("application/json")) {
|
|
862
|
-
info.putString("respType", "json");
|
|
863
|
-
} else {
|
|
864
|
-
info.putString("respType", "");
|
|
865
|
-
}
|
|
866
|
-
return info;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
/**
|
|
870
|
-
* Check if response data is binary data.
|
|
871
|
-
*
|
|
872
|
-
* @param resp OkHttp response.
|
|
873
|
-
* @return If the response data contains binary bytes
|
|
874
|
-
*/
|
|
875
|
-
private boolean isBlobResponse(Response resp) {
|
|
876
|
-
Headers h = resp.headers();
|
|
877
|
-
String ctype = getHeaderIgnoreCases(h, "Content-Type");
|
|
878
|
-
boolean isText = !ctype.equalsIgnoreCase("text/");
|
|
879
|
-
boolean isJSON = !ctype.equalsIgnoreCase("application/json");
|
|
880
|
-
boolean isCustomBinary = false;
|
|
881
|
-
if (options.binaryContentTypes != null) {
|
|
882
|
-
for (int i = 0; i < options.binaryContentTypes.size(); i++) {
|
|
883
|
-
if (ctype.toLowerCase(Locale.ROOT).contains(options.binaryContentTypes.getString(i).toLowerCase(Locale.ROOT))) {
|
|
884
|
-
isCustomBinary = true;
|
|
885
|
-
break;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
return (!(isJSON || isText)) || isCustomBinary;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
private String getHeaderIgnoreCases(Headers headers, String field) {
|
|
893
|
-
String val = headers.get(field);
|
|
894
|
-
if (val != null) return val;
|
|
895
|
-
return headers.get(field.toLowerCase(Locale.ROOT)) == null ? "" : headers.get(field.toLowerCase(Locale.ROOT));
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
private String getHeaderIgnoreCases(HashMap<String, String> headers, String field) {
|
|
899
|
-
String val = headers.get(field);
|
|
900
|
-
if (val != null) return val;
|
|
901
|
-
String lowerCasedValue = headers.get(field.toLowerCase(Locale.ROOT));
|
|
902
|
-
return lowerCasedValue == null ? "" : lowerCasedValue;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
private void emitStateEvent(WritableMap args) {
|
|
906
|
-
ReactNativeBlobUtilImpl.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
907
|
-
.emit(ReactNativeBlobUtilConst.EVENT_HTTP_STATE, args);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
@Override
|
|
911
|
-
public void onReceive(Context context, Intent intent) {
|
|
912
|
-
String action = intent.getAction();
|
|
913
|
-
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
|
|
914
|
-
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
915
|
-
long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
|
|
916
|
-
if (id == this.downloadManagerId) {
|
|
917
|
-
releaseTaskResource(); // remove task ID from task map
|
|
918
|
-
|
|
919
|
-
DownloadManager.Query query = new DownloadManager.Query();
|
|
920
|
-
query.setFilterById(downloadManagerId);
|
|
921
|
-
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
922
|
-
dm.query(query);
|
|
923
|
-
Cursor c = dm.query(query);
|
|
924
|
-
if (c == null) {
|
|
925
|
-
this.invoke_callback("Download manager failed to download from " + this.url + ". Query was unsuccessful ", null, null);
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
String filePath = null;
|
|
930
|
-
try {
|
|
931
|
-
// the file exists in media content database
|
|
932
|
-
if (c.moveToFirst()) {
|
|
933
|
-
int statusCode = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
|
|
934
|
-
if (statusCode == DownloadManager.STATUS_FAILED) {
|
|
935
|
-
this.invoke_callback("Download manager failed to download from " + this.url + ". Status Code = " + statusCode, null, null);
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
String contentUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
|
|
939
|
-
if (contentUri != null) {
|
|
940
|
-
Uri uri = Uri.parse(contentUri);
|
|
941
|
-
Cursor cursor = appCtx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Files.FileColumns.DATA}, null, null, null);
|
|
942
|
-
// use default destination of DownloadManager
|
|
943
|
-
if (cursor != null) {
|
|
944
|
-
cursor.moveToFirst();
|
|
945
|
-
filePath = cursor.getString(0);
|
|
946
|
-
cursor.close();
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
} finally {
|
|
951
|
-
if (c != null) {
|
|
952
|
-
c.close();
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// When the file is not found in media content database, check if custom path exists
|
|
957
|
-
if (options.addAndroidDownloads.hasKey("path") || options.addAndroidDownloads.hasKey("storeLocal")) {
|
|
958
|
-
try {
|
|
959
|
-
String customDest = customPath;
|
|
960
|
-
boolean exists = new File(customDest).exists();
|
|
961
|
-
if (!exists)
|
|
962
|
-
throw new Exception("Download manager download failed, the file does not downloaded to destination.");
|
|
963
|
-
else
|
|
964
|
-
this.invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, customDest);
|
|
965
|
-
|
|
966
|
-
} catch (Exception ex) {
|
|
967
|
-
ex.printStackTrace();
|
|
968
|
-
this.invoke_callback(ex.getLocalizedMessage(), null);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")){
|
|
972
|
-
Uri downloadeduri = dm.getUriForDownloadedFile(downloadManagerId);
|
|
973
|
-
if(downloadeduri == null)
|
|
974
|
-
this.invoke_callback("Download manager could not resolve downloaded file uri.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
|
|
975
|
-
else
|
|
976
|
-
this.invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, downloadeduri.toString());
|
|
977
|
-
}
|
|
978
|
-
else {
|
|
979
|
-
if (filePath == null)
|
|
980
|
-
this.invoke_callback("Download manager could not resolve downloaded file path.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
|
|
981
|
-
else
|
|
982
|
-
this.invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, filePath);
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
|
|
990
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
|
991
|
-
try {
|
|
992
|
-
// Code from https://github.com/square/okhttp/issues/2372#issuecomment-244807676
|
|
993
|
-
SSLContext sc = SSLContext.getInstance("TLSv1.2");
|
|
994
|
-
sc.init(null, null, null);
|
|
995
|
-
client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
|
|
996
|
-
|
|
997
|
-
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
|
998
|
-
.tlsVersions(TlsVersion.TLS_1_2)
|
|
999
|
-
.build();
|
|
1000
|
-
|
|
1001
|
-
List<ConnectionSpec> specs = new ArrayList<>();
|
|
1002
|
-
specs.add(cs);
|
|
1003
|
-
specs.add(ConnectionSpec.COMPATIBLE_TLS);
|
|
1004
|
-
specs.add(ConnectionSpec.CLEARTEXT);
|
|
1005
|
-
|
|
1006
|
-
client.connectionSpecs(specs);
|
|
1007
|
-
} catch (Exception exc) {
|
|
1008
|
-
FLog.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
return client;
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1
|
+
package com.ReactNativeBlobUtil;
|
|
2
|
+
|
|
3
|
+
import android.app.DownloadManager;
|
|
4
|
+
import android.content.BroadcastReceiver;
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import android.content.Intent;
|
|
7
|
+
import android.content.IntentFilter;
|
|
8
|
+
import android.database.Cursor;
|
|
9
|
+
import android.net.ConnectivityManager;
|
|
10
|
+
import android.net.Network;
|
|
11
|
+
import android.net.NetworkCapabilities;
|
|
12
|
+
import android.net.NetworkInfo;
|
|
13
|
+
import android.net.Uri;
|
|
14
|
+
import android.os.Build;
|
|
15
|
+
import android.os.Bundle;
|
|
16
|
+
import android.os.Environment;
|
|
17
|
+
import android.os.Handler;
|
|
18
|
+
import android.os.Message;
|
|
19
|
+
import android.util.Base64;
|
|
20
|
+
import android.webkit.CookieManager;
|
|
21
|
+
|
|
22
|
+
import androidx.annotation.NonNull;
|
|
23
|
+
|
|
24
|
+
import com.ReactNativeBlobUtil.Response.ReactNativeBlobUtilDefaultResp;
|
|
25
|
+
import com.ReactNativeBlobUtil.Response.ReactNativeBlobUtilFileResp;
|
|
26
|
+
import com.ReactNativeBlobUtil.Utils.Tls12SocketFactory;
|
|
27
|
+
import com.ReactNativeBlobUtil.ReactNativeBlobUtilFS;
|
|
28
|
+
import com.facebook.common.logging.FLog;
|
|
29
|
+
import com.facebook.react.bridge.Arguments;
|
|
30
|
+
import com.facebook.react.bridge.Callback;
|
|
31
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
32
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
33
|
+
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
|
34
|
+
import com.facebook.react.bridge.WritableArray;
|
|
35
|
+
import com.facebook.react.bridge.WritableMap;
|
|
36
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
37
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
38
|
+
|
|
39
|
+
import java.io.File;
|
|
40
|
+
import java.io.FileOutputStream;
|
|
41
|
+
import java.io.IOException;
|
|
42
|
+
import java.io.InputStream;
|
|
43
|
+
import java.net.MalformedURLException;
|
|
44
|
+
import java.net.Proxy;
|
|
45
|
+
import java.net.SocketException;
|
|
46
|
+
import java.net.SocketTimeoutException;
|
|
47
|
+
import java.net.URL;
|
|
48
|
+
import java.nio.ByteBuffer;
|
|
49
|
+
import java.nio.charset.CharacterCodingException;
|
|
50
|
+
import java.nio.charset.Charset;
|
|
51
|
+
import java.nio.charset.CharsetDecoder;
|
|
52
|
+
import java.util.ArrayList;
|
|
53
|
+
import java.util.HashMap;
|
|
54
|
+
|
|
55
|
+
import java.util.UUID;
|
|
56
|
+
|
|
57
|
+
import java.util.List;
|
|
58
|
+
import java.util.Locale;
|
|
59
|
+
import java.util.concurrent.Executors;
|
|
60
|
+
import java.util.concurrent.Future;
|
|
61
|
+
import java.util.concurrent.ScheduledExecutorService;
|
|
62
|
+
import java.util.concurrent.TimeUnit;
|
|
63
|
+
|
|
64
|
+
import javax.net.ssl.SSLContext;
|
|
65
|
+
|
|
66
|
+
import okhttp3.Call;
|
|
67
|
+
import okhttp3.ConnectionPool;
|
|
68
|
+
import okhttp3.ConnectionSpec;
|
|
69
|
+
import okhttp3.Headers;
|
|
70
|
+
import okhttp3.Interceptor;
|
|
71
|
+
import okhttp3.MediaType;
|
|
72
|
+
import okhttp3.OkHttpClient;
|
|
73
|
+
import okhttp3.Request;
|
|
74
|
+
import okhttp3.RequestBody;
|
|
75
|
+
import okhttp3.Response;
|
|
76
|
+
import okhttp3.ResponseBody;
|
|
77
|
+
import okhttp3.TlsVersion;
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
public class ReactNativeBlobUtilReq extends BroadcastReceiver implements Runnable {
|
|
81
|
+
|
|
82
|
+
enum RequestType {
|
|
83
|
+
Form,
|
|
84
|
+
SingleFile,
|
|
85
|
+
AsIs,
|
|
86
|
+
WithoutBody,
|
|
87
|
+
Others
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
enum ResponseType {
|
|
91
|
+
KeepInMemory,
|
|
92
|
+
FileStorage
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
enum ResponseFormat {
|
|
96
|
+
Auto,
|
|
97
|
+
UTF8,
|
|
98
|
+
BASE64
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private boolean shouldTransformFile() {
|
|
102
|
+
return this.options.transformFile &&
|
|
103
|
+
// Can only process if it's written to a file
|
|
104
|
+
(this.options.fileCache || this.options.path != null);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public static HashMap<String, Call> taskTable = new HashMap<>();
|
|
108
|
+
public static HashMap<String, Long> androidDownloadManagerTaskTable = new HashMap<>();
|
|
109
|
+
static HashMap<String, ReactNativeBlobUtilProgressConfig> progressReport = new HashMap<>();
|
|
110
|
+
static HashMap<String, ReactNativeBlobUtilProgressConfig> uploadProgressReport = new HashMap<>();
|
|
111
|
+
static ConnectionPool pool = new ConnectionPool();
|
|
112
|
+
|
|
113
|
+
ReactNativeBlobUtilConfig options;
|
|
114
|
+
String taskId;
|
|
115
|
+
String method;
|
|
116
|
+
String url;
|
|
117
|
+
String rawRequestBody;
|
|
118
|
+
String destPath;
|
|
119
|
+
String customPath;
|
|
120
|
+
ReadableArray rawRequestBodyArray;
|
|
121
|
+
ReadableMap headers;
|
|
122
|
+
Callback callback;
|
|
123
|
+
long contentLength;
|
|
124
|
+
long downloadManagerId;
|
|
125
|
+
ReactNativeBlobUtilBody requestBody;
|
|
126
|
+
RequestType requestType;
|
|
127
|
+
ResponseType responseType;
|
|
128
|
+
ResponseFormat responseFormat = ResponseFormat.Auto;
|
|
129
|
+
WritableMap respInfo;
|
|
130
|
+
boolean timeout = false;
|
|
131
|
+
ArrayList<String> redirects = new ArrayList<>();
|
|
132
|
+
OkHttpClient client;
|
|
133
|
+
boolean callbackfired;
|
|
134
|
+
|
|
135
|
+
public ReactNativeBlobUtilReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, OkHttpClient client, final Callback callback) {
|
|
136
|
+
this.method = method.toUpperCase(Locale.ROOT);
|
|
137
|
+
this.options = new ReactNativeBlobUtilConfig(options);
|
|
138
|
+
this.taskId = taskId;
|
|
139
|
+
this.url = url;
|
|
140
|
+
this.headers = headers;
|
|
141
|
+
this.callback = callback;
|
|
142
|
+
this.rawRequestBody = body;
|
|
143
|
+
this.rawRequestBodyArray = arrayBody;
|
|
144
|
+
this.client = client;
|
|
145
|
+
this.callbackfired = false;
|
|
146
|
+
|
|
147
|
+
// If transformFile is specified, we first want to get the response back in memory so we can
|
|
148
|
+
// encrypt it wholesale and at that point, write it into the file storage.
|
|
149
|
+
if((this.options.fileCache || this.options.path != null) && !this.shouldTransformFile())
|
|
150
|
+
responseType = ResponseType.FileStorage;
|
|
151
|
+
else
|
|
152
|
+
responseType = ResponseType.KeepInMemory;
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if (body != null)
|
|
156
|
+
requestType = RequestType.SingleFile;
|
|
157
|
+
else if (arrayBody != null)
|
|
158
|
+
requestType = RequestType.Form;
|
|
159
|
+
else
|
|
160
|
+
requestType = RequestType.WithoutBody;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public static void cancelTask(String taskId) {
|
|
164
|
+
Call call = taskTable.get(taskId);
|
|
165
|
+
if (call != null) {
|
|
166
|
+
call.cancel();
|
|
167
|
+
taskTable.remove(taskId);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (androidDownloadManagerTaskTable.containsKey(taskId)) {
|
|
171
|
+
long downloadManagerIdForTaskId = androidDownloadManagerTaskTable.get(taskId).longValue();
|
|
172
|
+
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
173
|
+
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
174
|
+
dm.remove(downloadManagerIdForTaskId);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private void invoke_callback(Object... args) {
|
|
179
|
+
if (this.callbackfired) return;
|
|
180
|
+
this.callback.invoke(args);
|
|
181
|
+
this.callbackfired = true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private final int QUERY = 1314;
|
|
185
|
+
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
|
186
|
+
private Future<?> future;
|
|
187
|
+
private Handler mHandler = new Handler(new Handler.Callback() {
|
|
188
|
+
public boolean handleMessage(Message msg) {
|
|
189
|
+
switch (msg.what) {
|
|
190
|
+
|
|
191
|
+
case QUERY:
|
|
192
|
+
|
|
193
|
+
Bundle data = msg.getData();
|
|
194
|
+
long id = data.getLong("downloadManagerId");
|
|
195
|
+
if (id == downloadManagerId) {
|
|
196
|
+
|
|
197
|
+
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
198
|
+
|
|
199
|
+
DownloadManager downloadManager = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
200
|
+
|
|
201
|
+
DownloadManager.Query query = new DownloadManager.Query();
|
|
202
|
+
query.setFilterById(downloadManagerId);
|
|
203
|
+
|
|
204
|
+
Cursor cursor = downloadManager.query(query);
|
|
205
|
+
|
|
206
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
207
|
+
|
|
208
|
+
long written = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
|
|
209
|
+
|
|
210
|
+
long total = cursor.getLong(cursor.getColumnIndex(
|
|
211
|
+
DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
|
|
212
|
+
cursor.close();
|
|
213
|
+
|
|
214
|
+
ReactNativeBlobUtilProgressConfig reportConfig = getReportProgress(taskId);
|
|
215
|
+
float progress = (total > 0) ? written / total : 0;
|
|
216
|
+
|
|
217
|
+
if (reportConfig != null && reportConfig.shouldReport(progress /* progress */)) {
|
|
218
|
+
WritableMap args = Arguments.createMap();
|
|
219
|
+
args.putString("taskId", String.valueOf(taskId));
|
|
220
|
+
args.putString("written", String.valueOf(written));
|
|
221
|
+
args.putString("total", String.valueOf(total));
|
|
222
|
+
args.putString("chunk", "");
|
|
223
|
+
ReactNativeBlobUtilImpl.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
224
|
+
.emit(ReactNativeBlobUtilConst.EVENT_PROGRESS, args);
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (total == written) {
|
|
229
|
+
future.cancel(true);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
@Override
|
|
239
|
+
public void run() {
|
|
240
|
+
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
241
|
+
// use download manager instead of default HTTP implementation
|
|
242
|
+
if (options.addAndroidDownloads != null && options.addAndroidDownloads.hasKey("useDownloadManager")) {
|
|
243
|
+
|
|
244
|
+
if (options.addAndroidDownloads.getBoolean("useDownloadManager")) {
|
|
245
|
+
Uri uri = Uri.parse(url);
|
|
246
|
+
DownloadManager.Request req = new DownloadManager.Request(uri);
|
|
247
|
+
if (options.addAndroidDownloads.hasKey("notification") && options.addAndroidDownloads.getBoolean("notification")) {
|
|
248
|
+
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
|
249
|
+
} else {
|
|
250
|
+
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
|
|
251
|
+
}
|
|
252
|
+
if (options.addAndroidDownloads.hasKey("title")) {
|
|
253
|
+
req.setTitle(options.addAndroidDownloads.getString("title"));
|
|
254
|
+
}
|
|
255
|
+
if (options.addAndroidDownloads.hasKey("description")) {
|
|
256
|
+
req.setDescription(options.addAndroidDownloads.getString("description"));
|
|
257
|
+
}
|
|
258
|
+
if (options.addAndroidDownloads.hasKey("path")) {
|
|
259
|
+
String path = options.addAndroidDownloads.getString("path");
|
|
260
|
+
File f = new File(path);
|
|
261
|
+
File dir = f.getParentFile();
|
|
262
|
+
|
|
263
|
+
if (!f.exists()) {
|
|
264
|
+
if (dir != null && !dir.exists()) {
|
|
265
|
+
if (!dir.mkdirs() && !dir.exists()) {
|
|
266
|
+
invoke_callback( "Failed to create parent directory of '" + path + "'", null, null);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
req.setDestinationUri(Uri.parse("file://" + path));
|
|
272
|
+
|
|
273
|
+
customPath = path;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
if (options.addAndroidDownloads.hasKey("storeLocal") && options.addAndroidDownloads.getBoolean("storeLocal")) {
|
|
278
|
+
String path = (String) ReactNativeBlobUtilFS.getSystemfolders(ReactNativeBlobUtilImpl.RCTContext).get("DownloadDir");
|
|
279
|
+
path = path + UUID.randomUUID().toString();
|
|
280
|
+
|
|
281
|
+
File f = new File(path);
|
|
282
|
+
File dir = f.getParentFile();
|
|
283
|
+
if (!f.exists()) {
|
|
284
|
+
if (dir != null && !dir.exists()) {
|
|
285
|
+
if (!dir.mkdirs() && !dir.exists()) {
|
|
286
|
+
invoke_callback( "Failed to create parent directory of '" + path + "'", null, null);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
req.setDestinationUri(Uri.parse("file://" + path));
|
|
292
|
+
customPath = path;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (options.addAndroidDownloads.hasKey("mime")) {
|
|
296
|
+
req.setMimeType(options.addAndroidDownloads.getString("mime"));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (options.addAndroidDownloads.hasKey("mediaScannable") && options.addAndroidDownloads.getBoolean("mediaScannable")) {
|
|
300
|
+
req.allowScanningByMediaScanner();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")) {
|
|
304
|
+
String t = options.addAndroidDownloads.getString("title");
|
|
305
|
+
if(t == null || t.isEmpty())
|
|
306
|
+
t = UUID.randomUUID().toString();
|
|
307
|
+
if(this.options.appendExt != null && !this.options.appendExt.isEmpty())
|
|
308
|
+
t += "." + this.options.appendExt;
|
|
309
|
+
|
|
310
|
+
req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, t);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// set headers
|
|
314
|
+
ReadableMapKeySetIterator it = headers.keySetIterator();
|
|
315
|
+
while (it.hasNextKey()) {
|
|
316
|
+
String key = it.nextKey();
|
|
317
|
+
req.addRequestHeader(key, headers.getString(key));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Attempt to add cookie, if it exists
|
|
321
|
+
URL urlObj;
|
|
322
|
+
try {
|
|
323
|
+
urlObj = new URL(url);
|
|
324
|
+
String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
|
|
325
|
+
String cookie = CookieManager.getInstance().getCookie(baseUrl);
|
|
326
|
+
req.addRequestHeader("Cookie", cookie);
|
|
327
|
+
} catch (MalformedURLException e) {
|
|
328
|
+
e.printStackTrace();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
332
|
+
downloadManagerId = dm.enqueue(req);
|
|
333
|
+
androidDownloadManagerTaskTable.put(taskId, Long.valueOf(downloadManagerId));
|
|
334
|
+
if(Build.VERSION.SDK_INT >= 34 ){
|
|
335
|
+
appCtx.registerReceiver(this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), Context.RECEIVER_EXPORTED);
|
|
336
|
+
}else{
|
|
337
|
+
appCtx.registerReceiver(this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
|
338
|
+
}
|
|
339
|
+
future = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
|
|
340
|
+
@Override
|
|
341
|
+
public void run() {
|
|
342
|
+
Message msg = mHandler.obtainMessage();
|
|
343
|
+
Bundle data = new Bundle();
|
|
344
|
+
data.putLong("downloadManagerId", downloadManagerId);
|
|
345
|
+
msg.setData(data);
|
|
346
|
+
msg.what = QUERY;
|
|
347
|
+
mHandler.sendMessage(msg);
|
|
348
|
+
}
|
|
349
|
+
}, 0, 100, TimeUnit.MILLISECONDS);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// find cached result if `key` property exists
|
|
356
|
+
String cacheKey = this.taskId;
|
|
357
|
+
String ext = (this.options.appendExt == null || this.options.appendExt.isEmpty()) ? "" : "." + this.options.appendExt;
|
|
358
|
+
|
|
359
|
+
if (this.options.key != null) {
|
|
360
|
+
cacheKey = ReactNativeBlobUtilUtils.getMD5(this.options.key);
|
|
361
|
+
if (cacheKey == null) {
|
|
362
|
+
cacheKey = this.taskId;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
File file = new File(ReactNativeBlobUtilFS.getTmpPath(cacheKey) + ext);
|
|
366
|
+
|
|
367
|
+
if (file.exists()) {
|
|
368
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, file.getAbsolutePath());
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (this.options.path != null)
|
|
374
|
+
this.destPath = this.options.path;
|
|
375
|
+
else if (this.options.fileCache)
|
|
376
|
+
this.destPath = ReactNativeBlobUtilFS.getTmpPath(cacheKey) + ext;
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
OkHttpClient.Builder clientBuilder;
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
// use trusty SSL socket
|
|
383
|
+
if (this.options.trusty) {
|
|
384
|
+
clientBuilder = ReactNativeBlobUtilUtils.getUnsafeOkHttpClient(client);
|
|
385
|
+
} else {
|
|
386
|
+
clientBuilder = client.newBuilder();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// wifi only, need ACCESS_NETWORK_STATE permission
|
|
390
|
+
// and API level >= 21
|
|
391
|
+
if (this.options.wifiOnly) {
|
|
392
|
+
|
|
393
|
+
boolean found = false;
|
|
394
|
+
|
|
395
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
396
|
+
ConnectivityManager connectivityManager = (ConnectivityManager) ReactNativeBlobUtilImpl.RCTContext.getSystemService(ReactNativeBlobUtilImpl.RCTContext.CONNECTIVITY_SERVICE);
|
|
397
|
+
Network[] networks = connectivityManager.getAllNetworks();
|
|
398
|
+
|
|
399
|
+
for (Network network : networks) {
|
|
400
|
+
|
|
401
|
+
NetworkInfo netInfo = connectivityManager.getNetworkInfo(network);
|
|
402
|
+
NetworkCapabilities caps = connectivityManager.getNetworkCapabilities(network);
|
|
403
|
+
|
|
404
|
+
if (caps == null || netInfo == null) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (!netInfo.isConnected()) {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
|
413
|
+
clientBuilder.proxy(Proxy.NO_PROXY);
|
|
414
|
+
clientBuilder.socketFactory(network.getSocketFactory());
|
|
415
|
+
found = true;
|
|
416
|
+
break;
|
|
417
|
+
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (!found) {
|
|
422
|
+
invoke_callback("No available WiFi connections.", null, null);
|
|
423
|
+
releaseTaskResource();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil: wifiOnly was set, but SDK < 21. wifiOnly was ignored.");
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
final Request.Builder builder = new Request.Builder();
|
|
432
|
+
try {
|
|
433
|
+
builder.url(new URL(url));
|
|
434
|
+
} catch (MalformedURLException e) {
|
|
435
|
+
e.printStackTrace();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
HashMap<String, String> mheaders = new HashMap<>();
|
|
439
|
+
// set headers
|
|
440
|
+
if (headers != null) {
|
|
441
|
+
ReadableMapKeySetIterator it = headers.keySetIterator();
|
|
442
|
+
while (it.hasNextKey()) {
|
|
443
|
+
String key = it.nextKey();
|
|
444
|
+
String value = headers.getString(key);
|
|
445
|
+
if (key.equalsIgnoreCase("RNFB-Response")) {
|
|
446
|
+
if (value.equalsIgnoreCase("base64"))
|
|
447
|
+
responseFormat = ResponseFormat.BASE64;
|
|
448
|
+
else if (value.equalsIgnoreCase("utf8"))
|
|
449
|
+
responseFormat = ResponseFormat.UTF8;
|
|
450
|
+
} else {
|
|
451
|
+
builder.header(key.toLowerCase(Locale.ROOT), value);
|
|
452
|
+
mheaders.put(key.toLowerCase(Locale.ROOT), value);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
|
|
458
|
+
String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase(Locale.ROOT);
|
|
459
|
+
|
|
460
|
+
if (rawRequestBodyArray != null) {
|
|
461
|
+
requestType = RequestType.Form;
|
|
462
|
+
} else if (cType == null || cType.isEmpty()) {
|
|
463
|
+
if (!cType.equalsIgnoreCase("")) {
|
|
464
|
+
builder.header("Content-Type", "application/octet-stream");
|
|
465
|
+
}
|
|
466
|
+
requestType = RequestType.SingleFile;
|
|
467
|
+
}
|
|
468
|
+
if (rawRequestBody != null) {
|
|
469
|
+
if (rawRequestBody.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX)
|
|
470
|
+
|| rawRequestBody.startsWith(ReactNativeBlobUtilConst.CONTENT_PREFIX)) {
|
|
471
|
+
requestType = RequestType.SingleFile;
|
|
472
|
+
} else if (cType.toLowerCase(Locale.ROOT).contains(";base64") || cType.toLowerCase(Locale.ROOT).startsWith("application/octet")) {
|
|
473
|
+
cType = cType.replace(";base64", "").replace(";BASE64", "");
|
|
474
|
+
if (mheaders.containsKey("content-type"))
|
|
475
|
+
mheaders.put("content-type", cType);
|
|
476
|
+
if (mheaders.containsKey("Content-Type"))
|
|
477
|
+
mheaders.put("Content-Type", cType);
|
|
478
|
+
requestType = RequestType.SingleFile;
|
|
479
|
+
} else {
|
|
480
|
+
requestType = RequestType.AsIs;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
} else {
|
|
484
|
+
requestType = RequestType.WithoutBody;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
boolean isChunkedRequest = getHeaderIgnoreCases(mheaders, "Transfer-Encoding").equalsIgnoreCase("chunked");
|
|
488
|
+
|
|
489
|
+
// set request body
|
|
490
|
+
switch (requestType) {
|
|
491
|
+
case SingleFile:
|
|
492
|
+
requestBody = new ReactNativeBlobUtilBody(taskId)
|
|
493
|
+
.chunkedEncoding(isChunkedRequest)
|
|
494
|
+
.setRequestType(requestType)
|
|
495
|
+
.setBody(rawRequestBody)
|
|
496
|
+
.setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
|
|
497
|
+
builder.method(method, requestBody);
|
|
498
|
+
break;
|
|
499
|
+
case AsIs:
|
|
500
|
+
requestBody = new ReactNativeBlobUtilBody(taskId)
|
|
501
|
+
.chunkedEncoding(isChunkedRequest)
|
|
502
|
+
.setRequestType(requestType)
|
|
503
|
+
.setBody(rawRequestBody)
|
|
504
|
+
.setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
|
|
505
|
+
builder.method(method, requestBody);
|
|
506
|
+
break;
|
|
507
|
+
case Form:
|
|
508
|
+
String boundary = "ReactNativeBlobUtil-" + taskId;
|
|
509
|
+
requestBody = new ReactNativeBlobUtilBody(taskId)
|
|
510
|
+
.chunkedEncoding(isChunkedRequest)
|
|
511
|
+
.setRequestType(requestType)
|
|
512
|
+
.setBody(rawRequestBodyArray)
|
|
513
|
+
.setMIME(MediaType.parse("multipart/form-data; boundary=" + boundary));
|
|
514
|
+
builder.method(method, requestBody);
|
|
515
|
+
break;
|
|
516
|
+
|
|
517
|
+
case WithoutBody:
|
|
518
|
+
if (method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
|
|
519
|
+
builder.method(method, RequestBody.create(null, new byte[0]));
|
|
520
|
+
} else
|
|
521
|
+
builder.method(method, null);
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// #156 fix cookie issue
|
|
526
|
+
final Request req = builder.build();
|
|
527
|
+
clientBuilder.addNetworkInterceptor(new Interceptor() {
|
|
528
|
+
@NonNull
|
|
529
|
+
@Override
|
|
530
|
+
public Response intercept(@NonNull Chain chain) throws IOException {
|
|
531
|
+
redirects.add(chain.request().url().toString());
|
|
532
|
+
return chain.proceed(chain.request());
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
// Add request interceptor for upload progress event
|
|
536
|
+
clientBuilder.addInterceptor(new Interceptor() {
|
|
537
|
+
@NonNull
|
|
538
|
+
@Override
|
|
539
|
+
public Response intercept(@NonNull Chain chain) throws IOException {
|
|
540
|
+
Response originalResponse = null;
|
|
541
|
+
try {
|
|
542
|
+
originalResponse = chain.proceed(req);
|
|
543
|
+
ResponseBody extended;
|
|
544
|
+
switch (responseType) {
|
|
545
|
+
case KeepInMemory:
|
|
546
|
+
extended = new ReactNativeBlobUtilDefaultResp(
|
|
547
|
+
ReactNativeBlobUtilImpl.RCTContext,
|
|
548
|
+
taskId,
|
|
549
|
+
originalResponse.body(),
|
|
550
|
+
options.increment);
|
|
551
|
+
break;
|
|
552
|
+
case FileStorage:
|
|
553
|
+
extended = new ReactNativeBlobUtilFileResp(
|
|
554
|
+
ReactNativeBlobUtilImpl.RCTContext,
|
|
555
|
+
taskId,
|
|
556
|
+
originalResponse.body(),
|
|
557
|
+
destPath,
|
|
558
|
+
options.overwrite);
|
|
559
|
+
break;
|
|
560
|
+
default:
|
|
561
|
+
extended = new ReactNativeBlobUtilDefaultResp(
|
|
562
|
+
ReactNativeBlobUtilImpl.RCTContext,
|
|
563
|
+
taskId,
|
|
564
|
+
originalResponse.body(),
|
|
565
|
+
options.increment);
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
return originalResponse.newBuilder().body(extended).build();
|
|
569
|
+
} catch (SocketException e) {
|
|
570
|
+
timeout = true;
|
|
571
|
+
if (originalResponse != null) {
|
|
572
|
+
originalResponse.close();
|
|
573
|
+
}
|
|
574
|
+
} catch (SocketTimeoutException e) {
|
|
575
|
+
timeout = true;
|
|
576
|
+
if (originalResponse != null) {
|
|
577
|
+
originalResponse.close();
|
|
578
|
+
}
|
|
579
|
+
//ReactNativeBlobUtilUtils.emitWarningEvent("ReactNativeBlobUtil error when sending request : " + e.getLocalizedMessage());
|
|
580
|
+
} catch (Exception ex) {
|
|
581
|
+
if (originalResponse != null) {
|
|
582
|
+
originalResponse.close();
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return chain.proceed(chain.request());
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
if (options.timeout >= 0) {
|
|
592
|
+
clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
|
|
593
|
+
clientBuilder.readTimeout(options.timeout, TimeUnit.MILLISECONDS);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
clientBuilder.connectionPool(pool);
|
|
597
|
+
clientBuilder.retryOnConnectionFailure(false);
|
|
598
|
+
clientBuilder.followRedirects(options.followRedirect);
|
|
599
|
+
clientBuilder.followSslRedirects(options.followRedirect);
|
|
600
|
+
clientBuilder.retryOnConnectionFailure(true);
|
|
601
|
+
|
|
602
|
+
OkHttpClient client = enableTls12OnPreLollipop(clientBuilder).build();
|
|
603
|
+
|
|
604
|
+
Call call = client.newCall(req);
|
|
605
|
+
taskTable.put(taskId, call);
|
|
606
|
+
call.enqueue(new okhttp3.Callback() {
|
|
607
|
+
|
|
608
|
+
@Override
|
|
609
|
+
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
610
|
+
cancelTask(taskId);
|
|
611
|
+
if (respInfo == null) {
|
|
612
|
+
respInfo = Arguments.createMap();
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// check if this error caused by socket timeout
|
|
616
|
+
if (e.getClass().equals(SocketTimeoutException.class)) {
|
|
617
|
+
respInfo.putBoolean("timeout", true);
|
|
618
|
+
invoke_callback("The request timed out.", null, null);
|
|
619
|
+
} else
|
|
620
|
+
invoke_callback(e.getLocalizedMessage(), null, null);
|
|
621
|
+
releaseTaskResource();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
@Override
|
|
625
|
+
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
|
|
626
|
+
ReadableMap notifyConfig = options.addAndroidDownloads;
|
|
627
|
+
// Download manager settings
|
|
628
|
+
if (notifyConfig != null) {
|
|
629
|
+
String title = "", desc = "", mime = "text/plain";
|
|
630
|
+
boolean scannable = false, notification = false;
|
|
631
|
+
if (notifyConfig.hasKey("title"))
|
|
632
|
+
title = options.addAndroidDownloads.getString("title");
|
|
633
|
+
if (notifyConfig.hasKey("description"))
|
|
634
|
+
desc = notifyConfig.getString("description");
|
|
635
|
+
if (notifyConfig.hasKey("mime"))
|
|
636
|
+
mime = notifyConfig.getString("mime");
|
|
637
|
+
if (notifyConfig.hasKey("mediaScannable"))
|
|
638
|
+
scannable = notifyConfig.getBoolean("mediaScannable");
|
|
639
|
+
if (notifyConfig.hasKey("notification"))
|
|
640
|
+
notification = notifyConfig.getBoolean("notification");
|
|
641
|
+
DownloadManager dm = (DownloadManager) ReactNativeBlobUtilImpl.RCTContext.getSystemService(ReactNativeBlobUtilImpl.RCTContext.DOWNLOAD_SERVICE);
|
|
642
|
+
dm.addCompletedDownload(title, desc, scannable, mime, destPath, contentLength, notification);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
done(response);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
} catch (Exception error) {
|
|
651
|
+
error.printStackTrace();
|
|
652
|
+
releaseTaskResource();
|
|
653
|
+
invoke_callback("ReactNativeBlobUtil request error: " + error.getMessage() + error.getCause());
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Remove cached information of the HTTP task
|
|
659
|
+
*/
|
|
660
|
+
private void releaseTaskResource() {
|
|
661
|
+
if (taskTable.containsKey(taskId))
|
|
662
|
+
taskTable.remove(taskId);
|
|
663
|
+
if (androidDownloadManagerTaskTable.containsKey(taskId))
|
|
664
|
+
androidDownloadManagerTaskTable.remove(taskId);
|
|
665
|
+
if (uploadProgressReport.containsKey(taskId))
|
|
666
|
+
uploadProgressReport.remove(taskId);
|
|
667
|
+
if (progressReport.containsKey(taskId))
|
|
668
|
+
progressReport.remove(taskId);
|
|
669
|
+
if (requestBody != null)
|
|
670
|
+
requestBody.clearRequestBody();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Send response data back to javascript context.
|
|
675
|
+
*
|
|
676
|
+
* @param resp OkHttp response object
|
|
677
|
+
*/
|
|
678
|
+
private void done(Response resp) {
|
|
679
|
+
boolean isBlobResp = isBlobResponse(resp);
|
|
680
|
+
WritableMap respmap = getResponseInfo(resp,isBlobResp);
|
|
681
|
+
emitStateEvent(respmap.copy());
|
|
682
|
+
|
|
683
|
+
emitStateEvent(getResponseInfo(resp, isBlobResp));
|
|
684
|
+
switch (responseType) {
|
|
685
|
+
case KeepInMemory:
|
|
686
|
+
try {
|
|
687
|
+
// For XMLHttpRequest, automatic response data storing strategy, when response
|
|
688
|
+
// data is considered as binary data, write it to file system
|
|
689
|
+
if (isBlobResp && options.auto) {
|
|
690
|
+
String dest = ReactNativeBlobUtilFS.getTmpPath(taskId);
|
|
691
|
+
InputStream ins = resp.body().byteStream();
|
|
692
|
+
FileOutputStream os = new FileOutputStream(new File(dest));
|
|
693
|
+
int read;
|
|
694
|
+
byte[] buffer = new byte[10240];
|
|
695
|
+
while ((read = ins.read(buffer)) != -1) {
|
|
696
|
+
os.write(buffer, 0, read);
|
|
697
|
+
}
|
|
698
|
+
ins.close();
|
|
699
|
+
os.flush();
|
|
700
|
+
os.close();
|
|
701
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, dest, respmap.copy());
|
|
702
|
+
}
|
|
703
|
+
// response data directly pass to JS context as string.
|
|
704
|
+
else {
|
|
705
|
+
byte[] b = resp.body().bytes();
|
|
706
|
+
// If process file option is turned on, we first keep response in memory and then stream that content
|
|
707
|
+
// after processing
|
|
708
|
+
if (this.shouldTransformFile()) {
|
|
709
|
+
if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
|
|
710
|
+
throw new IllegalStateException("Write file with transform was specified but the shared file transformer is not set");
|
|
711
|
+
}
|
|
712
|
+
this.destPath = this.destPath.replace("?append=true", "");
|
|
713
|
+
File file = new File(this.destPath);
|
|
714
|
+
if (!file.exists()) {
|
|
715
|
+
file.createNewFile();
|
|
716
|
+
}
|
|
717
|
+
try (FileOutputStream fos = new FileOutputStream(file)) {
|
|
718
|
+
fos.write(ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(b));
|
|
719
|
+
} catch (Exception e) {
|
|
720
|
+
invoke_callback("Error from file transformer:" + e.getLocalizedMessage(), respmap.copy());
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, this.destPath, respmap.copy());
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
if (responseFormat == ResponseFormat.BASE64) {
|
|
727
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP), respmap.copy());
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
try {
|
|
731
|
+
// Attempt to decode the incoming response data to determine whether it contains a valid UTF8 string
|
|
732
|
+
Charset charSet = Charset.forName("UTF-8");
|
|
733
|
+
CharsetDecoder decoder = charSet.newDecoder();
|
|
734
|
+
decoder.decode(ByteBuffer.wrap(b));
|
|
735
|
+
// If the data contains invalid characters the following lines will be skipped.
|
|
736
|
+
String utf8 = new String(b, charSet);
|
|
737
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_UTF8, utf8);
|
|
738
|
+
}
|
|
739
|
+
// This usually means the data contains invalid unicode characters but still valid data,
|
|
740
|
+
// it's binary data, so send it as a normal string
|
|
741
|
+
catch (CharacterCodingException ignored) {
|
|
742
|
+
if (responseFormat == ResponseFormat.UTF8) {
|
|
743
|
+
String utf8 = new String(b);
|
|
744
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_UTF8, utf8, respmap.copy());
|
|
745
|
+
} else {
|
|
746
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP), respmap.copy());
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
} catch (IOException e) {
|
|
751
|
+
invoke_callback("ReactNativeBlobUtil failed to encode response data to BASE64 string.", respmap.copy());
|
|
752
|
+
}
|
|
753
|
+
break;
|
|
754
|
+
case FileStorage:
|
|
755
|
+
ResponseBody responseBody = resp.body();
|
|
756
|
+
|
|
757
|
+
try {
|
|
758
|
+
// In order to write response data to `destPath` we have to invoke this method.
|
|
759
|
+
// It uses customized response body which is able to report download progress
|
|
760
|
+
// and write response data to destination path.
|
|
761
|
+
responseBody.bytes();
|
|
762
|
+
} catch (Exception ignored) {
|
|
763
|
+
// ignored.printStackTrace();
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
ReactNativeBlobUtilFileResp ReactNativeBlobUtilFileResp;
|
|
767
|
+
|
|
768
|
+
try {
|
|
769
|
+
ReactNativeBlobUtilFileResp = (ReactNativeBlobUtilFileResp) responseBody;
|
|
770
|
+
} catch (ClassCastException ex) {
|
|
771
|
+
// unexpected response type
|
|
772
|
+
if (responseBody != null) {
|
|
773
|
+
String responseBodyString = null;
|
|
774
|
+
try {
|
|
775
|
+
boolean isBufferDataExists = responseBody.source().buffer().size() > 0;
|
|
776
|
+
boolean isContentExists = responseBody.contentLength() > 0;
|
|
777
|
+
if (isBufferDataExists && isContentExists) {
|
|
778
|
+
responseBodyString = responseBody.string();
|
|
779
|
+
}
|
|
780
|
+
} catch (IOException exception) {
|
|
781
|
+
exception.printStackTrace();
|
|
782
|
+
}
|
|
783
|
+
invoke_callback("Unexpected FileStorage response file: " + responseBodyString, respmap.copy());
|
|
784
|
+
} else {
|
|
785
|
+
invoke_callback("Unexpected FileStorage response with no file.", respmap.copy());
|
|
786
|
+
}
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (ReactNativeBlobUtilFileResp != null && !ReactNativeBlobUtilFileResp.isDownloadComplete()) {
|
|
791
|
+
invoke_callback("Download interrupted.", respmap.copy());
|
|
792
|
+
} else {
|
|
793
|
+
this.destPath = this.destPath.replace("?append=true", "");
|
|
794
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, this.destPath, respmap.copy());
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
break;
|
|
798
|
+
default:
|
|
799
|
+
try {
|
|
800
|
+
invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_UTF8, new String(resp.body().bytes(), "UTF-8"), respmap.copy());
|
|
801
|
+
} catch (IOException e) {
|
|
802
|
+
invoke_callback("ReactNativeBlobUtil failed to encode response data to UTF8 string.", respmap.copy());
|
|
803
|
+
}
|
|
804
|
+
break;
|
|
805
|
+
}
|
|
806
|
+
// if(!resp.isSuccessful())
|
|
807
|
+
resp.body().close();
|
|
808
|
+
releaseTaskResource();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Invoke this method to enable download progress reporting.
|
|
813
|
+
*
|
|
814
|
+
* @param taskId Task ID of the HTTP task.
|
|
815
|
+
* @return Task ID of the target task
|
|
816
|
+
*/
|
|
817
|
+
public static ReactNativeBlobUtilProgressConfig getReportProgress(String taskId) {
|
|
818
|
+
if (!progressReport.containsKey(taskId)) return null;
|
|
819
|
+
return progressReport.get(taskId);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Invoke this method to enable download progress reporting.
|
|
824
|
+
*
|
|
825
|
+
* @param taskId Task ID of the HTTP task.
|
|
826
|
+
* @return Task ID of the target task
|
|
827
|
+
*/
|
|
828
|
+
public static ReactNativeBlobUtilProgressConfig getReportUploadProgress(String taskId) {
|
|
829
|
+
if (!uploadProgressReport.containsKey(taskId)) return null;
|
|
830
|
+
return uploadProgressReport.get(taskId);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Create response information object, contains status code, headers, etc.
|
|
835
|
+
*
|
|
836
|
+
* @param resp Response object
|
|
837
|
+
* @param isBlobResp If the response is binary data
|
|
838
|
+
* @return Get RCT bridge object contains response information.
|
|
839
|
+
*/
|
|
840
|
+
private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
|
|
841
|
+
WritableMap info = Arguments.createMap();
|
|
842
|
+
info.putInt("status", resp.code());
|
|
843
|
+
info.putString("state", "2");
|
|
844
|
+
info.putString("taskId", this.taskId);
|
|
845
|
+
info.putBoolean("timeout", timeout);
|
|
846
|
+
WritableMap headers = Arguments.createMap();
|
|
847
|
+
for (int i = 0; i < resp.headers().size(); i++) {
|
|
848
|
+
headers.putString(resp.headers().name(i), resp.headers().value(i));
|
|
849
|
+
}
|
|
850
|
+
WritableArray redirectList = Arguments.createArray();
|
|
851
|
+
for (String r : redirects) {
|
|
852
|
+
redirectList.pushString(r);
|
|
853
|
+
}
|
|
854
|
+
info.putArray("redirects", redirectList);
|
|
855
|
+
info.putMap("headers", headers);
|
|
856
|
+
Headers h = resp.headers();
|
|
857
|
+
if (isBlobResp) {
|
|
858
|
+
info.putString("respType", "blob");
|
|
859
|
+
} else if (getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/")) {
|
|
860
|
+
info.putString("respType", "text");
|
|
861
|
+
} else if (getHeaderIgnoreCases(h, "content-type").contains("application/json")) {
|
|
862
|
+
info.putString("respType", "json");
|
|
863
|
+
} else {
|
|
864
|
+
info.putString("respType", "");
|
|
865
|
+
}
|
|
866
|
+
return info;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Check if response data is binary data.
|
|
871
|
+
*
|
|
872
|
+
* @param resp OkHttp response.
|
|
873
|
+
* @return If the response data contains binary bytes
|
|
874
|
+
*/
|
|
875
|
+
private boolean isBlobResponse(Response resp) {
|
|
876
|
+
Headers h = resp.headers();
|
|
877
|
+
String ctype = getHeaderIgnoreCases(h, "Content-Type");
|
|
878
|
+
boolean isText = !ctype.equalsIgnoreCase("text/");
|
|
879
|
+
boolean isJSON = !ctype.equalsIgnoreCase("application/json");
|
|
880
|
+
boolean isCustomBinary = false;
|
|
881
|
+
if (options.binaryContentTypes != null) {
|
|
882
|
+
for (int i = 0; i < options.binaryContentTypes.size(); i++) {
|
|
883
|
+
if (ctype.toLowerCase(Locale.ROOT).contains(options.binaryContentTypes.getString(i).toLowerCase(Locale.ROOT))) {
|
|
884
|
+
isCustomBinary = true;
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return (!(isJSON || isText)) || isCustomBinary;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
private String getHeaderIgnoreCases(Headers headers, String field) {
|
|
893
|
+
String val = headers.get(field);
|
|
894
|
+
if (val != null) return val;
|
|
895
|
+
return headers.get(field.toLowerCase(Locale.ROOT)) == null ? "" : headers.get(field.toLowerCase(Locale.ROOT));
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
private String getHeaderIgnoreCases(HashMap<String, String> headers, String field) {
|
|
899
|
+
String val = headers.get(field);
|
|
900
|
+
if (val != null) return val;
|
|
901
|
+
String lowerCasedValue = headers.get(field.toLowerCase(Locale.ROOT));
|
|
902
|
+
return lowerCasedValue == null ? "" : lowerCasedValue;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
private void emitStateEvent(WritableMap args) {
|
|
906
|
+
ReactNativeBlobUtilImpl.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
907
|
+
.emit(ReactNativeBlobUtilConst.EVENT_HTTP_STATE, args);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
@Override
|
|
911
|
+
public void onReceive(Context context, Intent intent) {
|
|
912
|
+
String action = intent.getAction();
|
|
913
|
+
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
|
|
914
|
+
Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
|
|
915
|
+
long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
|
|
916
|
+
if (id == this.downloadManagerId) {
|
|
917
|
+
releaseTaskResource(); // remove task ID from task map
|
|
918
|
+
|
|
919
|
+
DownloadManager.Query query = new DownloadManager.Query();
|
|
920
|
+
query.setFilterById(downloadManagerId);
|
|
921
|
+
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
922
|
+
dm.query(query);
|
|
923
|
+
Cursor c = dm.query(query);
|
|
924
|
+
if (c == null) {
|
|
925
|
+
this.invoke_callback("Download manager failed to download from " + this.url + ". Query was unsuccessful ", null, null);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
String filePath = null;
|
|
930
|
+
try {
|
|
931
|
+
// the file exists in media content database
|
|
932
|
+
if (c.moveToFirst()) {
|
|
933
|
+
int statusCode = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
|
|
934
|
+
if (statusCode == DownloadManager.STATUS_FAILED) {
|
|
935
|
+
this.invoke_callback("Download manager failed to download from " + this.url + ". Status Code = " + statusCode, null, null);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
String contentUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
|
|
939
|
+
if (contentUri != null) {
|
|
940
|
+
Uri uri = Uri.parse(contentUri);
|
|
941
|
+
Cursor cursor = appCtx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Files.FileColumns.DATA}, null, null, null);
|
|
942
|
+
// use default destination of DownloadManager
|
|
943
|
+
if (cursor != null) {
|
|
944
|
+
cursor.moveToFirst();
|
|
945
|
+
filePath = cursor.getString(0);
|
|
946
|
+
cursor.close();
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
} finally {
|
|
951
|
+
if (c != null) {
|
|
952
|
+
c.close();
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// When the file is not found in media content database, check if custom path exists
|
|
957
|
+
if (options.addAndroidDownloads.hasKey("path") || options.addAndroidDownloads.hasKey("storeLocal")) {
|
|
958
|
+
try {
|
|
959
|
+
String customDest = customPath;
|
|
960
|
+
boolean exists = new File(customDest).exists();
|
|
961
|
+
if (!exists)
|
|
962
|
+
throw new Exception("Download manager download failed, the file does not downloaded to destination.");
|
|
963
|
+
else
|
|
964
|
+
this.invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, customDest);
|
|
965
|
+
|
|
966
|
+
} catch (Exception ex) {
|
|
967
|
+
ex.printStackTrace();
|
|
968
|
+
this.invoke_callback(ex.getLocalizedMessage(), null);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")){
|
|
972
|
+
Uri downloadeduri = dm.getUriForDownloadedFile(downloadManagerId);
|
|
973
|
+
if(downloadeduri == null)
|
|
974
|
+
this.invoke_callback("Download manager could not resolve downloaded file uri.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
|
|
975
|
+
else
|
|
976
|
+
this.invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, downloadeduri.toString());
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
if (filePath == null)
|
|
980
|
+
this.invoke_callback("Download manager could not resolve downloaded file path.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
|
|
981
|
+
else
|
|
982
|
+
this.invoke_callback(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, filePath);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
|
|
990
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
|
991
|
+
try {
|
|
992
|
+
// Code from https://github.com/square/okhttp/issues/2372#issuecomment-244807676
|
|
993
|
+
SSLContext sc = SSLContext.getInstance("TLSv1.2");
|
|
994
|
+
sc.init(null, null, null);
|
|
995
|
+
client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
|
|
996
|
+
|
|
997
|
+
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
|
998
|
+
.tlsVersions(TlsVersion.TLS_1_2)
|
|
999
|
+
.build();
|
|
1000
|
+
|
|
1001
|
+
List<ConnectionSpec> specs = new ArrayList<>();
|
|
1002
|
+
specs.add(cs);
|
|
1003
|
+
specs.add(ConnectionSpec.COMPATIBLE_TLS);
|
|
1004
|
+
specs.add(ConnectionSpec.CLEARTEXT);
|
|
1005
|
+
|
|
1006
|
+
client.connectionSpecs(specs);
|
|
1007
|
+
} catch (Exception exc) {
|
|
1008
|
+
FLog.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
return client;
|
|
1013
|
+
}
|
|
1014
|
+
}
|