@ammarahmed/react-native-upload 6.31.0 → 6.33.0
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/ios/RNFileUploader.m +320 -202
- package/package.json +1 -1
package/ios/RNFileUploader.m
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#import <Foundation/Foundation.h>
|
|
2
2
|
#import <MobileCoreServices/MobileCoreServices.h>
|
|
3
|
-
#import <React/RCTEventEmitter.h>
|
|
4
|
-
#import <React/RCTBridgeModule.h>
|
|
5
3
|
#import <Photos/Photos.h>
|
|
4
|
+
#import <React/RCTBridgeModule.h>
|
|
5
|
+
#import <React/RCTEventEmitter.h>
|
|
6
6
|
|
|
7
7
|
#import "RNFileUploader.h"
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
RCT_EXPORT_MODULE();
|
|
12
12
|
|
|
13
13
|
@synthesize bridge = _bridge;
|
|
14
|
-
static RNFileUploader*
|
|
14
|
+
static RNFileUploader *staticInstance = nil;
|
|
15
15
|
static NSString *BACKGROUND_SESSION_ID = @"ReactNativeBackgroundUpload";
|
|
16
16
|
NSMutableDictionary *_responsesData;
|
|
17
17
|
NSURLSession *_urlSession = nil;
|
|
@@ -22,12 +22,11 @@ BOOL limitNetwork = NO;
|
|
|
22
22
|
return YES;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
- (dispatch_queue_t)methodQueue
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
25
|
+
- (dispatch_queue_t)methodQueue {
|
|
26
|
+
return dispatch_get_main_queue();
|
|
27
|
+
}
|
|
29
28
|
|
|
30
|
-
-(id)
|
|
29
|
+
- (id)init {
|
|
31
30
|
self = [super init];
|
|
32
31
|
if (self) {
|
|
33
32
|
staticInstance = self;
|
|
@@ -37,84 +36,98 @@ BOOL limitNetwork = NO;
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
- (void)_sendEventWithName:(NSString *)eventName body:(id)body {
|
|
40
|
-
if (staticInstance == nil)
|
|
39
|
+
if (staticInstance == nil)
|
|
40
|
+
return;
|
|
41
41
|
[staticInstance sendEventWithName:eventName body:body];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
- (NSArray<NSString *> *)supportedEvents {
|
|
45
45
|
return @[
|
|
46
|
-
@"RNFileUploader-progress",
|
|
47
|
-
@"RNFileUploader-
|
|
48
|
-
@"RNFileUploader-cancelled",
|
|
49
|
-
@"RNFileUploader-completed"
|
|
46
|
+
@"RNFileUploader-progress", @"RNFileUploader-error",
|
|
47
|
+
@"RNFileUploader-cancelled", @"RNFileUploader-completed"
|
|
50
48
|
];
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
- (void)startObserving {
|
|
54
|
-
// JS side is ready to receive events; create the background url session if
|
|
55
|
-
// iOS will then deliver the tasks completed while the app was
|
|
52
|
+
// JS side is ready to receive events; create the background url session if
|
|
53
|
+
// necessary iOS will then deliver the tasks completed while the app was
|
|
54
|
+
// dead (if any)
|
|
56
55
|
NSString *appGroup = nil;
|
|
57
56
|
double delayInSeconds = 5;
|
|
58
|
-
dispatch_time_t popTime =
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
dispatch_time_t popTime =
|
|
58
|
+
dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
|
|
59
|
+
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
|
|
60
|
+
[self urlSession:appGroup];
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
+ (void)setCompletionHandlerWithIdentifier:
|
|
64
|
+
+ (void)setCompletionHandlerWithIdentifier:(NSString *)identifier
|
|
65
|
+
completionHandler:(void (^)())completionHandler {
|
|
65
66
|
if ([BACKGROUND_SESSION_ID isEqualToString:identifier]) {
|
|
66
67
|
backgroundSessionCompletionHandler = completionHandler;
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
/*
|
|
71
|
-
Gets file information for the path specified. Example valid path is:
|
|
72
|
-
|
|
72
|
+
Gets file information for the path specified. Example valid path is:
|
|
73
|
+
file:///var/mobile/Containers/Data/Application/3C8A0EFB-A316-45C0-A30A-761BF8CCF2F8/tmp/trim.A5F76017-14E9-4890-907E-36A045AF9436.MOV
|
|
74
|
+
Returns an object such as: {mimeType: "video/quicktime", size: 2569900, exists:
|
|
75
|
+
true, name: "trim.AF9A9225-FC37-416B-A25B-4EDB8275A625.MOV", extension: "MOV"}
|
|
73
76
|
*/
|
|
74
|
-
RCT_EXPORT_METHOD(getFileInfo:(NSString *)path resolve:
|
|
75
|
-
{
|
|
77
|
+
RCT_EXPORT_METHOD(getFileInfo : (NSString *)path resolve : (
|
|
78
|
+
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) {
|
|
76
79
|
@try {
|
|
77
80
|
// Escape non latin characters in filename
|
|
78
|
-
NSString *escapedPath =
|
|
79
|
-
|
|
81
|
+
NSString *escapedPath =
|
|
82
|
+
[path stringByAddingPercentEncodingWithAllowedCharacters:
|
|
83
|
+
NSCharacterSet.URLQueryAllowedCharacterSet];
|
|
84
|
+
|
|
80
85
|
NSURL *fileUri = [NSURL URLWithString:escapedPath];
|
|
81
86
|
NSString *pathWithoutProtocol = [fileUri path];
|
|
82
87
|
NSString *name = [fileUri lastPathComponent];
|
|
83
88
|
NSString *extension = [name pathExtension];
|
|
84
|
-
bool exists = [[NSFileManager defaultManager]
|
|
85
|
-
|
|
89
|
+
bool exists = [[NSFileManager defaultManager]
|
|
90
|
+
fileExistsAtPath:pathWithoutProtocol];
|
|
91
|
+
NSMutableDictionary *params = [NSMutableDictionary
|
|
92
|
+
dictionaryWithObjectsAndKeys:name, @"name", nil];
|
|
86
93
|
[params setObject:extension forKey:@"extension"];
|
|
87
94
|
[params setObject:[NSNumber numberWithBool:exists] forKey:@"exists"];
|
|
88
95
|
|
|
89
|
-
if (exists)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
NSError*
|
|
93
|
-
NSDictionary<NSFileAttributeKey, id> *attributes =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
if (exists) {
|
|
97
|
+
[params setObject:[self guessMIMETypeFromFileName:name]
|
|
98
|
+
forKey:@"mimeType"];
|
|
99
|
+
NSError *error;
|
|
100
|
+
NSDictionary<NSFileAttributeKey, id> *attributes =
|
|
101
|
+
[[NSFileManager defaultManager]
|
|
102
|
+
attributesOfItemAtPath:pathWithoutProtocol
|
|
103
|
+
error:&error];
|
|
104
|
+
if (error == nil) {
|
|
96
105
|
unsigned long long fileSize = [attributes fileSize];
|
|
97
|
-
[params setObject:[NSNumber numberWithLong:fileSize]
|
|
106
|
+
[params setObject:[NSNumber numberWithLong:fileSize]
|
|
107
|
+
forKey:@"size"];
|
|
98
108
|
}
|
|
99
109
|
}
|
|
100
110
|
resolve(params);
|
|
101
|
-
}
|
|
102
|
-
@catch (NSException *exception) {
|
|
111
|
+
} @catch (NSException *exception) {
|
|
103
112
|
reject(@"RN Uploader", exception.name, nil);
|
|
104
113
|
}
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
/*
|
|
108
|
-
Borrowed from
|
|
117
|
+
Borrowed from
|
|
118
|
+
http://stackoverflow.com/questions/2439020/wheres-the-iphone-mime-type-database
|
|
109
119
|
*/
|
|
110
|
-
- (NSString *)guessMIMETypeFromFileName:
|
|
111
|
-
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
- (NSString *)guessMIMETypeFromFileName:(NSString *)fileName {
|
|
121
|
+
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(
|
|
122
|
+
kUTTagClassFilenameExtension,
|
|
123
|
+
(__bridge CFStringRef)[fileName pathExtension], NULL);
|
|
124
|
+
CFStringRef MIMEType =
|
|
125
|
+
UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
|
|
126
|
+
|
|
114
127
|
if (UTI) {
|
|
115
128
|
CFRelease(UTI);
|
|
116
129
|
}
|
|
117
|
-
|
|
130
|
+
|
|
118
131
|
if (!MIMEType) {
|
|
119
132
|
return @"application/octet-stream";
|
|
120
133
|
}
|
|
@@ -122,33 +135,48 @@ RCT_EXPORT_METHOD(getFileInfo:(NSString *)path resolve:(RCTPromiseResolveBlock)r
|
|
|
122
135
|
}
|
|
123
136
|
|
|
124
137
|
/*
|
|
125
|
-
Utility method to copy a PHAsset file into a local temp file, which can then be
|
|
138
|
+
Utility method to copy a PHAsset file into a local temp file, which can then be
|
|
139
|
+
uploaded.
|
|
126
140
|
*/
|
|
127
|
-
- (void)copyAssetToFile:
|
|
141
|
+
- (void)copyAssetToFile:(NSString *)assetUrl
|
|
142
|
+
completionHandler:(void (^)(NSString *__nullable tempFileUrl,
|
|
143
|
+
NSError *__nullable error))completionHandler {
|
|
128
144
|
NSURL *url = [NSURL URLWithString:assetUrl];
|
|
129
|
-
PHAsset *asset =
|
|
145
|
+
PHAsset *asset =
|
|
146
|
+
[PHAsset fetchAssetsWithALAssetURLs:@[ url ] options:nil].lastObject;
|
|
130
147
|
if (!asset) {
|
|
131
|
-
NSMutableDictionary*
|
|
132
|
-
[details
|
|
133
|
-
|
|
148
|
+
NSMutableDictionary *details = [NSMutableDictionary dictionary];
|
|
149
|
+
[details
|
|
150
|
+
setValue:
|
|
151
|
+
@"Asset could not be fetched. Are you missing permissions?"
|
|
152
|
+
forKey:NSLocalizedDescriptionKey];
|
|
153
|
+
completionHandler(nil, [NSError errorWithDomain:@"RNUploader"
|
|
154
|
+
code:5
|
|
155
|
+
userInfo:details]);
|
|
134
156
|
return;
|
|
135
157
|
}
|
|
136
|
-
PHAssetResource *assetResource =
|
|
137
|
-
|
|
158
|
+
PHAssetResource *assetResource =
|
|
159
|
+
[[PHAssetResource assetResourcesForAsset:asset] firstObject];
|
|
160
|
+
NSString *pathToWrite = [NSTemporaryDirectory()
|
|
161
|
+
stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
|
|
138
162
|
NSURL *pathUrl = [NSURL fileURLWithPath:pathToWrite];
|
|
139
163
|
NSString *fileURI = pathUrl.absoluteString;
|
|
140
164
|
|
|
141
|
-
PHAssetResourceRequestOptions *options =
|
|
165
|
+
PHAssetResourceRequestOptions *options =
|
|
166
|
+
[PHAssetResourceRequestOptions new];
|
|
142
167
|
options.networkAccessAllowed = YES;
|
|
143
168
|
|
|
144
|
-
[[PHAssetResourceManager defaultManager]
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
[[PHAssetResourceManager defaultManager]
|
|
170
|
+
writeDataForAssetResource:assetResource
|
|
171
|
+
toFile:pathUrl
|
|
172
|
+
options:options
|
|
173
|
+
completionHandler:^(NSError *_Nullable e) {
|
|
174
|
+
if (e == nil) {
|
|
175
|
+
completionHandler(fileURI, nil);
|
|
176
|
+
} else {
|
|
177
|
+
completionHandler(nil, e);
|
|
178
|
+
}
|
|
179
|
+
}];
|
|
152
180
|
}
|
|
153
181
|
|
|
154
182
|
/*
|
|
@@ -162,8 +190,8 @@ RCT_EXPORT_METHOD(getFileInfo:(NSString *)path resolve:(RCTPromiseResolveBlock)r
|
|
|
162
190
|
*
|
|
163
191
|
* Returns a promise with the string ID of the upload.
|
|
164
192
|
*/
|
|
165
|
-
RCT_EXPORT_METHOD(startUpload:(NSDictionary *)options resolve:
|
|
166
|
-
{
|
|
193
|
+
RCT_EXPORT_METHOD(startUpload : (NSDictionary *)options resolve : (
|
|
194
|
+
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) {
|
|
167
195
|
NSString *uploadUrl = options[@"url"];
|
|
168
196
|
__block NSString *fileURI = options[@"path"];
|
|
169
197
|
NSString *method = options[@"method"] ?: @"POST";
|
|
@@ -175,68 +203,89 @@ RCT_EXPORT_METHOD(startUpload:(NSDictionary *)options resolve:(RCTPromiseResolve
|
|
|
175
203
|
NSDictionary *parameters = options[@"parameters"];
|
|
176
204
|
|
|
177
205
|
@try {
|
|
178
|
-
NSURL *requestUrl = [NSURL URLWithString:
|
|
206
|
+
NSURL *requestUrl = [NSURL URLWithString:uploadUrl];
|
|
179
207
|
if (requestUrl == nil) {
|
|
180
|
-
return reject(@"RN Uploader", @"URL not compliant with RFC 2396",
|
|
208
|
+
return reject(@"RN Uploader", @"URL not compliant with RFC 2396",
|
|
209
|
+
nil);
|
|
181
210
|
}
|
|
182
211
|
|
|
183
|
-
NSMutableURLRequest *request =
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
212
|
+
NSMutableURLRequest *request =
|
|
213
|
+
[NSMutableURLRequest requestWithURL:requestUrl];
|
|
214
|
+
[request setHTTPMethod:method];
|
|
215
|
+
|
|
216
|
+
[headers enumerateKeysAndObjectsUsingBlock:^(
|
|
217
|
+
id _Nonnull key, id _Nonnull val, BOOL *_Nonnull stop) {
|
|
218
|
+
if ([val respondsToSelector:@selector(stringValue)]) {
|
|
219
|
+
val = [val stringValue];
|
|
220
|
+
}
|
|
221
|
+
if ([val isKindOfClass:[NSString class]]) {
|
|
222
|
+
[request setValue:val forHTTPHeaderField:key];
|
|
223
|
+
}
|
|
193
224
|
}];
|
|
194
225
|
|
|
195
|
-
|
|
196
|
-
//
|
|
226
|
+
// asset library files have to be copied over to a temp file. they
|
|
227
|
+
// can't be uploaded directly
|
|
197
228
|
if ([fileURI hasPrefix:@"assets-library"]) {
|
|
198
229
|
dispatch_group_t group = dispatch_group_create();
|
|
199
230
|
dispatch_group_enter(group);
|
|
200
|
-
[self copyAssetToFile:fileURI
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
231
|
+
[self copyAssetToFile:fileURI
|
|
232
|
+
completionHandler:^(NSString *_Nullable tempFileUrl,
|
|
233
|
+
NSError *_Nullable error) {
|
|
234
|
+
if (error) {
|
|
235
|
+
dispatch_group_leave(group);
|
|
236
|
+
reject(@"RN Uploader",
|
|
237
|
+
@"Asset could not be copied to temp file.", nil);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
fileURI = tempFileUrl;
|
|
241
|
+
dispatch_group_leave(group);
|
|
242
|
+
}];
|
|
209
243
|
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
|
210
244
|
}
|
|
211
245
|
|
|
212
|
-
NSString *uploadId =
|
|
246
|
+
NSString *uploadId =
|
|
247
|
+
customUploadId ? customUploadId : [[NSUUID UUID] UUIDString];
|
|
213
248
|
NSURLSessionUploadTask *uploadTask;
|
|
214
249
|
|
|
215
250
|
if ([uploadType isEqualToString:@"multipart"]) {
|
|
216
251
|
NSString *uuidStr = [[NSUUID UUID] UUIDString];
|
|
217
|
-
[request setValue:[NSString stringWithFormat
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
252
|
+
[request setValue:[NSString stringWithFormat:
|
|
253
|
+
@"multipart/form-data; boundary=%@",
|
|
254
|
+
uuidStr]
|
|
255
|
+
forHTTPHeaderField:@"Content-Type"];
|
|
256
|
+
|
|
257
|
+
NSData *multipartData = [self createBodyWithBoundary:uuidStr
|
|
258
|
+
path:fileURI
|
|
259
|
+
parameters:parameters
|
|
260
|
+
fieldName:fieldName];
|
|
261
|
+
|
|
262
|
+
NSURL *multipartDataFileUrl = [NSURL
|
|
263
|
+
fileURLWithPath:[NSString
|
|
264
|
+
stringWithFormat:@"%@/%@",
|
|
265
|
+
[self getTmpDirectory],
|
|
266
|
+
uploadId]];
|
|
222
267
|
[multipartData writeToURL:multipartDataFileUrl atomically:YES];
|
|
223
268
|
|
|
224
|
-
uploadTask = [[self urlSession:
|
|
269
|
+
uploadTask = [[self urlSession:appGroup]
|
|
270
|
+
uploadTaskWithRequest:request
|
|
271
|
+
fromFile:multipartDataFileUrl];
|
|
225
272
|
} else {
|
|
226
273
|
if (parameters.count > 0) {
|
|
227
|
-
reject(@"RN Uploader",
|
|
274
|
+
reject(@"RN Uploader",
|
|
275
|
+
@"Parameters supported only in multipart type", nil);
|
|
228
276
|
return;
|
|
229
277
|
}
|
|
230
278
|
|
|
231
|
-
uploadTask = [[self urlSession:
|
|
279
|
+
uploadTask = [[self urlSession:appGroup]
|
|
280
|
+
uploadTaskWithRequest:request
|
|
281
|
+
fromFile:[NSURL URLWithString:fileURI]];
|
|
232
282
|
}
|
|
233
283
|
|
|
234
284
|
uploadTask.taskDescription = uploadId;
|
|
235
285
|
|
|
236
286
|
[uploadTask resume];
|
|
237
287
|
resolve(uploadTask.taskDescription);
|
|
238
|
-
}
|
|
239
|
-
@catch (NSException *exception) {
|
|
288
|
+
} @catch (NSException *exception) {
|
|
240
289
|
reject(@"RN Uploader", exception.name, nil);
|
|
241
290
|
}
|
|
242
291
|
}
|
|
@@ -246,14 +295,22 @@ RCT_EXPORT_METHOD(startUpload:(NSDictionary *)options resolve:(RCTPromiseResolve
|
|
|
246
295
|
* Accepts upload ID as a first argument, this upload will be cancelled
|
|
247
296
|
* Event "cancelled" will be fired when upload is cancelled.
|
|
248
297
|
*/
|
|
249
|
-
RCT_EXPORT_METHOD(cancelUpload: (NSString *)cancelUploadId resolve:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
298
|
+
RCT_EXPORT_METHOD(cancelUpload : (NSString *)cancelUploadId resolve : (
|
|
299
|
+
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) {
|
|
300
|
+
if (_urlSession == nil) {
|
|
301
|
+
resolve([NSNumber numberWithBool:NO]);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
[_urlSession getTasksWithCompletionHandler:^(NSArray *dataTasks,
|
|
305
|
+
NSArray *uploadTasks,
|
|
306
|
+
NSArray *downloadTasks) {
|
|
307
|
+
for (NSURLSessionTask *uploadTask in uploadTasks) {
|
|
308
|
+
if ([uploadTask.taskDescription isEqualToString:cancelUploadId]) {
|
|
309
|
+
// == checks if references are equal, while isEqualToString checks
|
|
310
|
+
// the string value
|
|
311
|
+
[uploadTask cancel];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
257
314
|
}];
|
|
258
315
|
resolve([NSNumber numberWithBool:YES]);
|
|
259
316
|
}
|
|
@@ -265,93 +322,130 @@ RCT_EXPORT_METHOD(canSuspendIfBackground) {
|
|
|
265
322
|
}
|
|
266
323
|
}
|
|
267
324
|
|
|
268
|
-
RCT_EXPORT_METHOD(shouldLimitNetwork: (BOOL)
|
|
269
|
-
limitNetwork = limit;
|
|
270
|
-
}
|
|
325
|
+
RCT_EXPORT_METHOD(shouldLimitNetwork : (BOOL)limit) { limitNetwork = limit; }
|
|
271
326
|
|
|
272
|
-
RCT_EXPORT_METHOD(getAllUploads:(RCTPromiseResolveBlock)
|
|
273
|
-
|
|
274
|
-
{
|
|
327
|
+
RCT_EXPORT_METHOD(getAllUploads : (RCTPromiseResolveBlock)
|
|
328
|
+
resolve reject : (RCTPromiseRejectBlock)reject) {
|
|
275
329
|
NSString *appGroup = nil;
|
|
276
|
-
[[self urlSession:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
330
|
+
[[self urlSession:appGroup]
|
|
331
|
+
getTasksWithCompletionHandler:^(
|
|
332
|
+
NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
|
|
333
|
+
NSMutableArray *uploads = [NSMutableArray new];
|
|
334
|
+
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
|
|
335
|
+
@try {
|
|
336
|
+
NSString *state = @"unknown"; // Default initialization
|
|
337
|
+
switch (uploadTask.state) {
|
|
338
|
+
case NSURLSessionTaskStateRunning:
|
|
339
|
+
state = @"running";
|
|
340
|
+
break;
|
|
341
|
+
case NSURLSessionTaskStateSuspended:
|
|
342
|
+
state = @"pending";
|
|
343
|
+
break;
|
|
344
|
+
case NSURLSessionTaskStateCanceling:
|
|
345
|
+
state = @"cancelled";
|
|
346
|
+
break;
|
|
347
|
+
case NSURLSessionTaskStateCompleted:
|
|
348
|
+
state = @"completed";
|
|
349
|
+
break;
|
|
350
|
+
default: // Handle unexpected states
|
|
351
|
+
state = @"unknown";
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
NSDictionary *upload = @{
|
|
356
|
+
@"id" : uploadTask.taskDescription ?: @"", // Null safety
|
|
357
|
+
@"state" : state
|
|
358
|
+
};
|
|
359
|
+
[uploads addObject:upload];
|
|
360
|
+
} @catch (NSException *exception) {
|
|
361
|
+
NSDictionary *upload = @{ @"id" : @"", @"state" : @"unknown" };
|
|
362
|
+
[uploads addObject:upload];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
resolve(uploads);
|
|
366
|
+
}];
|
|
303
367
|
}
|
|
304
368
|
|
|
305
369
|
- (NSData *)createBodyWithBoundary:(NSString *)boundary
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
370
|
+
path:(NSString *)path
|
|
371
|
+
parameters:(NSDictionary *)parameters
|
|
372
|
+
fieldName:(NSString *)fieldName {
|
|
309
373
|
|
|
310
374
|
NSMutableData *httpBody = [NSMutableData data];
|
|
311
375
|
|
|
312
376
|
// Escape non latin characters in filename
|
|
313
|
-
NSString *escapedPath =
|
|
377
|
+
NSString *escapedPath =
|
|
378
|
+
[path stringByAddingPercentEncodingWithAllowedCharacters:
|
|
379
|
+
NSCharacterSet.URLQueryAllowedCharacterSet];
|
|
314
380
|
|
|
315
381
|
// resolve path
|
|
316
|
-
NSURL *fileUri = [NSURL URLWithString:
|
|
317
|
-
|
|
318
|
-
NSError*
|
|
319
|
-
NSData *data = [NSData dataWithContentsOfURL:fileUri
|
|
382
|
+
NSURL *fileUri = [NSURL URLWithString:escapedPath];
|
|
383
|
+
|
|
384
|
+
NSError *error = nil;
|
|
385
|
+
NSData *data = [NSData dataWithContentsOfURL:fileUri
|
|
386
|
+
options:NSDataReadingMappedAlways
|
|
387
|
+
error:&error];
|
|
320
388
|
|
|
321
389
|
if (data == nil) {
|
|
322
390
|
NSLog(@"Failed to read file %@", error);
|
|
323
391
|
}
|
|
324
392
|
|
|
325
|
-
NSString *filename
|
|
326
|
-
NSString *mimetype
|
|
327
|
-
|
|
328
|
-
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
393
|
+
NSString *filename = [path lastPathComponent];
|
|
394
|
+
NSString *mimetype = [self guessMIMETypeFromFileName:path];
|
|
395
|
+
|
|
396
|
+
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey,
|
|
397
|
+
NSString *parameterValue,
|
|
398
|
+
BOOL *stop) {
|
|
399
|
+
[httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
|
|
400
|
+
dataUsingEncoding:NSUTF8StringEncoding]];
|
|
401
|
+
[httpBody
|
|
402
|
+
appendData:[[NSString
|
|
403
|
+
stringWithFormat:@"Content-Disposition: form-data; "
|
|
404
|
+
@"name=\"%@\"\r\n\r\n",
|
|
405
|
+
parameterKey]
|
|
406
|
+
dataUsingEncoding:NSUTF8StringEncoding]];
|
|
407
|
+
[httpBody
|
|
408
|
+
appendData:[[NSString stringWithFormat:@"%@\r\n", parameterValue]
|
|
409
|
+
dataUsingEncoding:NSUTF8StringEncoding]];
|
|
332
410
|
}];
|
|
333
411
|
|
|
334
|
-
[httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
|
|
335
|
-
|
|
336
|
-
[httpBody
|
|
412
|
+
[httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
|
|
413
|
+
dataUsingEncoding:NSUTF8StringEncoding]];
|
|
414
|
+
[httpBody
|
|
415
|
+
appendData:[[NSString
|
|
416
|
+
stringWithFormat:@"Content-Disposition: form-data; "
|
|
417
|
+
@"name=\"%@\"; filename=\"%@\"\r\n",
|
|
418
|
+
fieldName, filename]
|
|
419
|
+
dataUsingEncoding:NSUTF8StringEncoding]];
|
|
420
|
+
[httpBody
|
|
421
|
+
appendData:[[NSString
|
|
422
|
+
stringWithFormat:@"Content-Type: %@\r\n\r\n", mimetype]
|
|
423
|
+
dataUsingEncoding:NSUTF8StringEncoding]];
|
|
337
424
|
[httpBody appendData:data];
|
|
338
425
|
[httpBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
|
339
426
|
|
|
340
|
-
[httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
|
|
427
|
+
[httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
|
|
428
|
+
dataUsingEncoding:NSUTF8StringEncoding]];
|
|
341
429
|
|
|
342
430
|
return httpBody;
|
|
343
431
|
}
|
|
344
432
|
|
|
345
|
-
- (NSURLSession *)urlSession:
|
|
433
|
+
- (NSURLSession *)urlSession:(NSString *)groupId {
|
|
346
434
|
if (_urlSession == nil) {
|
|
347
|
-
NSURLSessionConfiguration *sessionConfiguration =
|
|
435
|
+
NSURLSessionConfiguration *sessionConfiguration =
|
|
436
|
+
[NSURLSessionConfiguration
|
|
437
|
+
backgroundSessionConfigurationWithIdentifier:
|
|
438
|
+
BACKGROUND_SESSION_ID];
|
|
348
439
|
if (groupId != nil && ![groupId isEqualToString:@""]) {
|
|
349
440
|
sessionConfiguration.sharedContainerIdentifier = groupId;
|
|
350
441
|
}
|
|
351
442
|
if (limitNetwork) {
|
|
352
443
|
sessionConfiguration.allowsCellularAccess = NO;
|
|
353
444
|
}
|
|
354
|
-
_urlSession =
|
|
445
|
+
_urlSession =
|
|
446
|
+
[NSURLSession sessionWithConfiguration:sessionConfiguration
|
|
447
|
+
delegate:self
|
|
448
|
+
delegateQueue:nil];
|
|
355
449
|
}
|
|
356
450
|
|
|
357
451
|
return _urlSession;
|
|
@@ -360,41 +454,45 @@ RCT_EXPORT_METHOD(getAllUploads:(RCTPromiseResolveBlock)resolve
|
|
|
360
454
|
- (NSString *)getTmpDirectory {
|
|
361
455
|
NSFileManager *manager = [NSFileManager defaultManager];
|
|
362
456
|
NSString *namespace = @"react-native-upload";
|
|
363
|
-
NSString *tmpPath =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
457
|
+
NSString *tmpPath =
|
|
458
|
+
[NSTemporaryDirectory() stringByAppendingString:namespace];
|
|
459
|
+
|
|
460
|
+
[manager createDirectoryAtPath:tmpPath
|
|
461
|
+
withIntermediateDirectories:YES
|
|
462
|
+
attributes:nil
|
|
463
|
+
error:nil];
|
|
464
|
+
|
|
367
465
|
return tmpPath;
|
|
368
466
|
}
|
|
369
467
|
|
|
370
468
|
#pragma mark - NSURLSessionTaskDelegate
|
|
371
469
|
|
|
372
470
|
- (void)URLSession:(NSURLSession *)session
|
|
373
|
-
|
|
374
|
-
didCompleteWithError:(NSError *)error {
|
|
375
|
-
NSMutableDictionary *data = [NSMutableDictionary
|
|
471
|
+
task:(NSURLSessionTask *)task
|
|
472
|
+
didCompleteWithError:(NSError *)error {
|
|
473
|
+
NSMutableDictionary *data = [NSMutableDictionary
|
|
474
|
+
dictionaryWithObjectsAndKeys:task.taskDescription, @"id", nil];
|
|
376
475
|
NSURLSessionDataTask *uploadTask = (NSURLSessionDataTask *)task;
|
|
377
476
|
NSHTTPURLResponse *response = (NSHTTPURLResponse *)uploadTask.response;
|
|
378
|
-
if (response != nil)
|
|
379
|
-
|
|
380
|
-
|
|
477
|
+
if (response != nil) {
|
|
478
|
+
[data setObject:[NSNumber numberWithInteger:response.statusCode]
|
|
479
|
+
forKey:@"responseCode"];
|
|
381
480
|
}
|
|
382
|
-
//Add data that was collected earlier by the didReceiveData method
|
|
481
|
+
// Add data that was collected earlier by the didReceiveData method
|
|
383
482
|
NSMutableData *responseData = _responsesData[@(task.taskIdentifier)];
|
|
384
483
|
if (responseData) {
|
|
385
484
|
[_responsesData removeObjectForKey:@(task.taskIdentifier)];
|
|
386
|
-
NSString *response =
|
|
485
|
+
NSString *response =
|
|
486
|
+
[[NSString alloc] initWithData:responseData
|
|
487
|
+
encoding:NSUTF8StringEncoding];
|
|
387
488
|
[data setObject:response forKey:@"responseBody"];
|
|
388
489
|
} else {
|
|
389
490
|
[data setObject:[NSNull null] forKey:@"responseBody"];
|
|
390
491
|
}
|
|
391
492
|
|
|
392
|
-
if (error == nil)
|
|
393
|
-
{
|
|
493
|
+
if (error == nil) {
|
|
394
494
|
[self _sendEventWithName:@"RNFileUploader-completed" body:data];
|
|
395
|
-
}
|
|
396
|
-
else
|
|
397
|
-
{
|
|
495
|
+
} else {
|
|
398
496
|
[data setObject:error.localizedDescription forKey:@"error"];
|
|
399
497
|
if (error.code == NSURLErrorCancelled) {
|
|
400
498
|
[self _sendEventWithName:@"RNFileUploader-cancelled" body:data];
|
|
@@ -403,32 +501,46 @@ didCompleteWithError:(NSError *)error {
|
|
|
403
501
|
}
|
|
404
502
|
}
|
|
405
503
|
|
|
406
|
-
NSURL *multipartDataFileUrl = [NSURL
|
|
407
|
-
|
|
504
|
+
NSURL *multipartDataFileUrl = [NSURL
|
|
505
|
+
fileURLWithPath:[NSString stringWithFormat:@"%@/%@",
|
|
506
|
+
[self getTmpDirectory],
|
|
507
|
+
task.taskDescription]];
|
|
508
|
+
[[NSFileManager defaultManager] removeItemAtURL:multipartDataFileUrl
|
|
509
|
+
error:nil];
|
|
408
510
|
}
|
|
409
511
|
|
|
410
512
|
- (void)URLSession:(NSURLSession *)session
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
|
|
513
|
+
task:(NSURLSessionTask *)task
|
|
514
|
+
didSendBodyData:(int64_t)bytesSent
|
|
515
|
+
totalBytesSent:(int64_t)totalBytesSent
|
|
516
|
+
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
|
|
415
517
|
float progress = -1;
|
|
416
|
-
if (totalBytesExpectedToSend >
|
|
518
|
+
if (totalBytesExpectedToSend >
|
|
519
|
+
0) // see documentation. For unknown size it's -1
|
|
520
|
+
// (NSURLSessionTransferSizeUnknown)
|
|
417
521
|
{
|
|
418
|
-
progress =
|
|
522
|
+
progress =
|
|
523
|
+
100.0 * (float)totalBytesSent / (float)totalBytesExpectedToSend;
|
|
419
524
|
}
|
|
420
|
-
[self _sendEventWithName:@"RNFileUploader-progress"
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
525
|
+
[self _sendEventWithName:@"RNFileUploader-progress"
|
|
526
|
+
body:@{
|
|
527
|
+
@"id" : task.taskDescription,
|
|
528
|
+
@"progress" : [NSNumber numberWithFloat:progress],
|
|
529
|
+
@"totalBytes" : [NSNumber
|
|
530
|
+
numberWithInteger:totalBytesExpectedToSend],
|
|
531
|
+
@"uploadedBytes" :
|
|
532
|
+
[NSNumber numberWithInteger:totalBytesSent]
|
|
533
|
+
}];
|
|
425
534
|
}
|
|
426
535
|
|
|
427
|
-
- (void)URLSession:(NSURLSession *)session
|
|
536
|
+
- (void)URLSession:(NSURLSession *)session
|
|
537
|
+
dataTask:(NSURLSessionDataTask *)dataTask
|
|
538
|
+
didReceiveData:(NSData *)data {
|
|
428
539
|
if (!data.length) {
|
|
429
540
|
return;
|
|
430
541
|
}
|
|
431
|
-
//Hold returned data so it can be picked up by the didCompleteWithError
|
|
542
|
+
// Hold returned data so it can be picked up by the didCompleteWithError
|
|
543
|
+
// method later
|
|
432
544
|
NSMutableData *responseData = _responsesData[@(dataTask.taskIdentifier)];
|
|
433
545
|
if (!responseData) {
|
|
434
546
|
responseData = [NSMutableData dataWithData:data];
|
|
@@ -440,21 +552,27 @@ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
|
|
|
440
552
|
|
|
441
553
|
#pragma mark - NSURLSessionDelegate
|
|
442
554
|
|
|
443
|
-
- (void)URLSessionDidFinishEventsForBackgroundURLSession:
|
|
555
|
+
- (void)URLSessionDidFinishEventsForBackgroundURLSession:
|
|
556
|
+
(NSURLSession *)session {
|
|
444
557
|
if (backgroundSessionCompletionHandler) {
|
|
445
|
-
NSLog(@"RNBU Did Finish Events For Background URLSession (has
|
|
446
|
-
|
|
558
|
+
NSLog(@"RNBU Did Finish Events For Background URLSession (has "
|
|
559
|
+
@"backgroundSessionCompletionHandler)");
|
|
560
|
+
// This long delay is set as a security if the JS side does not call
|
|
561
|
+
// :canSuspendIfBackground: promptly
|
|
447
562
|
double delayInSeconds = 45.0;
|
|
448
|
-
dispatch_time_t popTime =
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
563
|
+
dispatch_time_t popTime =
|
|
564
|
+
dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
|
|
565
|
+
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
|
|
566
|
+
if (backgroundSessionCompletionHandler) {
|
|
567
|
+
backgroundSessionCompletionHandler();
|
|
568
|
+
NSLog(@"RNBU did call backgroundSessionCompletionHandler "
|
|
569
|
+
@"(timeout)");
|
|
570
|
+
backgroundSessionCompletionHandler = nil;
|
|
571
|
+
}
|
|
455
572
|
});
|
|
456
573
|
} else {
|
|
457
|
-
NSLog(@"RNBU Did Finish Events For Background URLSession (no
|
|
574
|
+
NSLog(@"RNBU Did Finish Events For Background URLSession (no "
|
|
575
|
+
@"backgroundSessionCompletionHandler)");
|
|
458
576
|
}
|
|
459
577
|
}
|
|
460
578
|
|
package/package.json
CHANGED