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