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,292 +1,292 @@
|
|
|
1
|
-
//
|
|
2
|
-
// ReactNativeBlobUtilReqBuilder.m
|
|
3
|
-
// ReactNativeBlobUtil
|
|
4
|
-
//
|
|
5
|
-
// Created by Ben Hsieh on 2016/7/9.
|
|
6
|
-
// Copyright © 2016年 wkh237. All rights reserved.
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
#import <Foundation/Foundation.h>
|
|
10
|
-
#import "ReactNativeBlobUtilReqBuilder.h"
|
|
11
|
-
#import "ReactNativeBlobUtilNetwork.h"
|
|
12
|
-
#import "ReactNativeBlobUtilConst.h"
|
|
13
|
-
#import "ReactNativeBlobUtilFS.h"
|
|
14
|
-
|
|
15
|
-
#if __has_include(<React/RCTAssert.h>)
|
|
16
|
-
#import <React/RCTLog.h>
|
|
17
|
-
#else
|
|
18
|
-
#import "RCTLog.h"
|
|
19
|
-
#endif
|
|
20
|
-
|
|
21
|
-
@interface ReactNativeBlobUtilReqBuilder()
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
@end
|
|
26
|
-
|
|
27
|
-
@implementation ReactNativeBlobUtilReqBuilder
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// Fetch blob data request
|
|
31
|
-
+(void) buildMultipartRequest:(NSDictionary *)options
|
|
32
|
-
taskId:(NSString *)taskId
|
|
33
|
-
method:(NSString *)method
|
|
34
|
-
url:(NSString *)url
|
|
35
|
-
headers:(NSDictionary *)headers
|
|
36
|
-
form:(NSArray *)form
|
|
37
|
-
onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete
|
|
38
|
-
{
|
|
39
|
-
// NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
40
|
-
NSString * encodedUrl = url;
|
|
41
|
-
|
|
42
|
-
// send request
|
|
43
|
-
__block NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString: encodedUrl]];
|
|
44
|
-
__block NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[ReactNativeBlobUtilNetwork normalizeHeaders:headers]];
|
|
45
|
-
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
|
|
46
|
-
NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
|
|
47
|
-
|
|
48
|
-
// generate boundary
|
|
49
|
-
__block NSString * boundary = [NSString stringWithFormat:@"ReactNativeBlobUtil%d", timeStampObj];
|
|
50
|
-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
51
|
-
__block NSMutableData * postData = [[NSMutableData alloc] init];
|
|
52
|
-
// combine multipart/form-data body
|
|
53
|
-
[[self class] buildFormBody:form boundary:boundary onComplete:^(NSData *formData, BOOL hasError) {
|
|
54
|
-
if(hasError)
|
|
55
|
-
{
|
|
56
|
-
onComplete(nil, 0);
|
|
57
|
-
}
|
|
58
|
-
else
|
|
59
|
-
{
|
|
60
|
-
if(formData != nil)
|
|
61
|
-
{
|
|
62
|
-
[postData appendData:formData];
|
|
63
|
-
// close form data
|
|
64
|
-
[postData appendData: [[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
65
|
-
[request setHTTPBody:postData];
|
|
66
|
-
}
|
|
67
|
-
// set content-length
|
|
68
|
-
[mheaders setValue:[NSString stringWithFormat:@"%lu",[postData length]] forKey:@"Content-Length"];
|
|
69
|
-
[mheaders setValue:@"100-continue" forKey:@"Expect"];
|
|
70
|
-
// appaned boundary to content-type
|
|
71
|
-
[mheaders setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forKey:@"content-type"];
|
|
72
|
-
[request setHTTPMethod: method];
|
|
73
|
-
[request setAllHTTPHeaderFields:mheaders];
|
|
74
|
-
onComplete(request, [formData length]);
|
|
75
|
-
}
|
|
76
|
-
}];
|
|
77
|
-
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Fetch blob data request
|
|
82
|
-
+(void) buildOctetRequest:(NSDictionary *)options
|
|
83
|
-
taskId:(NSString *)taskId
|
|
84
|
-
method:(NSString *)method
|
|
85
|
-
url:(NSString *)url
|
|
86
|
-
headers:(NSDictionary *)headers
|
|
87
|
-
body:(NSString *)body
|
|
88
|
-
onComplete:(void(^)(__weak NSURLRequest * req, long bodyLength))onComplete
|
|
89
|
-
{
|
|
90
|
-
// NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
91
|
-
NSString * encodedUrl = url;
|
|
92
|
-
// send request
|
|
93
|
-
__block NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
|
|
94
|
-
initWithURL:[NSURL URLWithString: encodedUrl]];
|
|
95
|
-
|
|
96
|
-
__block NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[ReactNativeBlobUtilNetwork normalizeHeaders:headers]];
|
|
97
|
-
// move heavy task to another thread
|
|
98
|
-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
99
|
-
NSMutableData * blobData;
|
|
100
|
-
long size = -1;
|
|
101
|
-
// if method is POST, PUT or PATCH, convert data string format
|
|
102
|
-
if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"] || [[method lowercaseString] isEqualToString:@"patch"]) {
|
|
103
|
-
// generate octet-stream body
|
|
104
|
-
if(body != nil) {
|
|
105
|
-
__block NSString * cType = [[self class] getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
|
|
106
|
-
__block NSString * transferEncoding = [[self class] getHeaderIgnoreCases:@"transfer-encoding" fromHeaders:mheaders];
|
|
107
|
-
// when headers does not contain a key named "content-type" (case ignored), use default content type
|
|
108
|
-
if(cType == nil)
|
|
109
|
-
{
|
|
110
|
-
[mheaders setValue:@"application/octet-stream" forKey:@"Content-Type"];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// when body is a string contains file path prefix, try load file from the path
|
|
114
|
-
if([body hasPrefix:FILE_PREFIX]) {
|
|
115
|
-
__block NSString * orgPath = [body substringFromIndex:[FILE_PREFIX length]];
|
|
116
|
-
orgPath = [ReactNativeBlobUtilFS getPathOfAsset:orgPath];
|
|
117
|
-
orgPath = [[NSURL URLWithString:orgPath] path];
|
|
118
|
-
if([orgPath hasPrefix:AL_PREFIX])
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
[ReactNativeBlobUtilFS readFile:orgPath encoding:nil transformFile:false onComplete:^(id content, NSString* code, NSString * err) {
|
|
122
|
-
if(err != nil)
|
|
123
|
-
{
|
|
124
|
-
onComplete(nil, 0);
|
|
125
|
-
}
|
|
126
|
-
else
|
|
127
|
-
{
|
|
128
|
-
[request setHTTPBody:((NSData *)content)];
|
|
129
|
-
[request setHTTPMethod: method];
|
|
130
|
-
[request setAllHTTPHeaderFields:mheaders];
|
|
131
|
-
onComplete(request, [((NSData *)content) length]);
|
|
132
|
-
}
|
|
133
|
-
}];
|
|
134
|
-
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
size = [[[NSFileManager defaultManager] attributesOfItemAtPath:orgPath error:nil] fileSize];
|
|
138
|
-
if(transferEncoding != nil && [[transferEncoding lowercaseString] isEqualToString:@"chunked"])
|
|
139
|
-
{
|
|
140
|
-
[request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
|
|
141
|
-
}
|
|
142
|
-
else
|
|
143
|
-
{
|
|
144
|
-
__block NSData * bodyBytes = [NSData dataWithContentsOfFile:orgPath ];
|
|
145
|
-
[request setHTTPBody:bodyBytes];
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
// otherwise convert it as BASE64 data string
|
|
149
|
-
else {
|
|
150
|
-
|
|
151
|
-
__block NSString * cType = [[self class]getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
|
|
152
|
-
// when content-type is application/octet* decode body string using BASE64 decoder
|
|
153
|
-
if([[cType lowercaseString] hasPrefix:@"application/octet"] || [[cType lowercaseString] containsString:@";base64"])
|
|
154
|
-
{
|
|
155
|
-
__block NSString * ncType = [[cType stringByReplacingOccurrencesOfString:@";base64" withString:@""]stringByReplacingOccurrencesOfString:@";BASE64" withString:@""];
|
|
156
|
-
if([mheaders valueForKey:@"content-type"] != nil)
|
|
157
|
-
[mheaders setValue:ncType forKey:@"content-type"];
|
|
158
|
-
if([mheaders valueForKey:@"Content-Type"] != nil)
|
|
159
|
-
[mheaders setValue:ncType forKey:@"Content-Type"];
|
|
160
|
-
blobData = [[NSData alloc] initWithBase64EncodedString:body options:0];
|
|
161
|
-
[request setHTTPBody:blobData];
|
|
162
|
-
size = [blobData length];
|
|
163
|
-
}
|
|
164
|
-
// otherwise use the body as-is
|
|
165
|
-
else
|
|
166
|
-
{
|
|
167
|
-
size = [body length];
|
|
168
|
-
[request setHTTPBody: [body dataUsingEncoding:NSUTF8StringEncoding]];
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
[request setHTTPMethod: method];
|
|
175
|
-
[request setAllHTTPHeaderFields:mheaders];
|
|
176
|
-
|
|
177
|
-
onComplete(request, size);
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
+(void) buildFormBody:(NSArray *)form boundary:(NSString *)boundary onComplete:(void(^)(NSData * formData, BOOL hasError))onComplete
|
|
182
|
-
{
|
|
183
|
-
__block NSMutableData * formData = [[NSMutableData alloc] init];
|
|
184
|
-
if(form == nil)
|
|
185
|
-
onComplete(nil, NO);
|
|
186
|
-
else
|
|
187
|
-
{
|
|
188
|
-
__block int i = 0;
|
|
189
|
-
__block int count = [form count];
|
|
190
|
-
// a recursive block that builds multipart body asynchornously
|
|
191
|
-
void __block (^getFieldData)(id field) = ^(id field)
|
|
192
|
-
{
|
|
193
|
-
NSString * name = [field valueForKey:@"name"];
|
|
194
|
-
__block NSString * content = [field valueForKey:@"data"];
|
|
195
|
-
NSString * contentType = [field valueForKey:@"type"];
|
|
196
|
-
// skip when the form field `name` or `data` is empty
|
|
197
|
-
if(content == nil || name == nil)
|
|
198
|
-
{
|
|
199
|
-
i++;
|
|
200
|
-
getFieldData([form objectAtIndex:i]);
|
|
201
|
-
RCTLogWarn(@"ReactNativeBlobUtil multipart request builder has found a field without `data` or `name` property, the field will be removed implicitly.");
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// field is a text field
|
|
206
|
-
if([field valueForKey:@"filename"] == nil || content == nil) {
|
|
207
|
-
contentType = contentType == nil ? @"text/plain" : contentType;
|
|
208
|
-
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
209
|
-
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", name] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
210
|
-
[formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
211
|
-
[formData appendData:[[NSString stringWithFormat:@"%@\r\n", content] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
212
|
-
}
|
|
213
|
-
// field contains a file
|
|
214
|
-
else {
|
|
215
|
-
contentType = contentType == nil ? @"application/octet-stream" : contentType;
|
|
216
|
-
NSMutableData * blobData;
|
|
217
|
-
if(content != nil)
|
|
218
|
-
{
|
|
219
|
-
// append data from file asynchronously
|
|
220
|
-
if([content hasPrefix:FILE_PREFIX])
|
|
221
|
-
{
|
|
222
|
-
NSString * orgPath = [content substringFromIndex:[FILE_PREFIX length]];
|
|
223
|
-
orgPath = [ReactNativeBlobUtilFS getPathOfAsset:orgPath];
|
|
224
|
-
|
|
225
|
-
[ReactNativeBlobUtilFS readFile:orgPath encoding:nil transformFile:false onComplete:^(NSData *content, NSString* code, NSString * err) {
|
|
226
|
-
if(err != nil)
|
|
227
|
-
{
|
|
228
|
-
onComplete(formData, YES);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
NSString * filename = [field valueForKey:@"filename"];
|
|
232
|
-
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
233
|
-
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
234
|
-
[formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
235
|
-
[formData appendData:content];
|
|
236
|
-
[formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
237
|
-
i++;
|
|
238
|
-
if(i < count)
|
|
239
|
-
{
|
|
240
|
-
__block NSDictionary * nextField = [form objectAtIndex:i];
|
|
241
|
-
getFieldData(nextField);
|
|
242
|
-
}
|
|
243
|
-
else
|
|
244
|
-
{
|
|
245
|
-
onComplete(formData, NO);
|
|
246
|
-
getFieldData = nil;
|
|
247
|
-
}
|
|
248
|
-
}];
|
|
249
|
-
return ;
|
|
250
|
-
}
|
|
251
|
-
else
|
|
252
|
-
blobData = [[NSData alloc] initWithBase64EncodedString:content options:0];
|
|
253
|
-
}
|
|
254
|
-
NSString * filename = [field valueForKey:@"filename"];
|
|
255
|
-
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
256
|
-
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
257
|
-
[formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
258
|
-
[formData appendData:blobData];
|
|
259
|
-
[formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
260
|
-
blobData = nil;
|
|
261
|
-
}
|
|
262
|
-
i++;
|
|
263
|
-
if(i < count)
|
|
264
|
-
{
|
|
265
|
-
__block NSDictionary * nextField = [form objectAtIndex:i];
|
|
266
|
-
getFieldData(nextField);
|
|
267
|
-
}
|
|
268
|
-
else
|
|
269
|
-
{
|
|
270
|
-
onComplete(formData, NO);
|
|
271
|
-
getFieldData = nil;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
};
|
|
275
|
-
__block NSDictionary * nextField = [form objectAtIndex:i];
|
|
276
|
-
getFieldData(nextField);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers {
|
|
281
|
-
|
|
282
|
-
NSString * normalCase = [headers valueForKey:field];
|
|
283
|
-
NSString * ignoredCase = [headers valueForKey:[field lowercaseString]];
|
|
284
|
-
if( normalCase != nil)
|
|
285
|
-
return normalCase;
|
|
286
|
-
else
|
|
287
|
-
return ignoredCase;
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
@end
|
|
1
|
+
//
|
|
2
|
+
// ReactNativeBlobUtilReqBuilder.m
|
|
3
|
+
// ReactNativeBlobUtil
|
|
4
|
+
//
|
|
5
|
+
// Created by Ben Hsieh on 2016/7/9.
|
|
6
|
+
// Copyright © 2016年 wkh237. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import <Foundation/Foundation.h>
|
|
10
|
+
#import "ReactNativeBlobUtilReqBuilder.h"
|
|
11
|
+
#import "ReactNativeBlobUtilNetwork.h"
|
|
12
|
+
#import "ReactNativeBlobUtilConst.h"
|
|
13
|
+
#import "ReactNativeBlobUtilFS.h"
|
|
14
|
+
|
|
15
|
+
#if __has_include(<React/RCTAssert.h>)
|
|
16
|
+
#import <React/RCTLog.h>
|
|
17
|
+
#else
|
|
18
|
+
#import "RCTLog.h"
|
|
19
|
+
#endif
|
|
20
|
+
|
|
21
|
+
@interface ReactNativeBlobUtilReqBuilder()
|
|
22
|
+
{
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
@end
|
|
26
|
+
|
|
27
|
+
@implementation ReactNativeBlobUtilReqBuilder
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
// Fetch blob data request
|
|
31
|
+
+(void) buildMultipartRequest:(NSDictionary *)options
|
|
32
|
+
taskId:(NSString *)taskId
|
|
33
|
+
method:(NSString *)method
|
|
34
|
+
url:(NSString *)url
|
|
35
|
+
headers:(NSDictionary *)headers
|
|
36
|
+
form:(NSArray *)form
|
|
37
|
+
onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete
|
|
38
|
+
{
|
|
39
|
+
// NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
40
|
+
NSString * encodedUrl = url;
|
|
41
|
+
|
|
42
|
+
// send request
|
|
43
|
+
__block NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString: encodedUrl]];
|
|
44
|
+
__block NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[ReactNativeBlobUtilNetwork normalizeHeaders:headers]];
|
|
45
|
+
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
|
|
46
|
+
NSNumber * timeStampObj = [NSNumber numberWithDouble: timeStamp];
|
|
47
|
+
|
|
48
|
+
// generate boundary
|
|
49
|
+
__block NSString * boundary = [NSString stringWithFormat:@"ReactNativeBlobUtil%d", timeStampObj];
|
|
50
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
51
|
+
__block NSMutableData * postData = [[NSMutableData alloc] init];
|
|
52
|
+
// combine multipart/form-data body
|
|
53
|
+
[[self class] buildFormBody:form boundary:boundary onComplete:^(NSData *formData, BOOL hasError) {
|
|
54
|
+
if(hasError)
|
|
55
|
+
{
|
|
56
|
+
onComplete(nil, 0);
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
{
|
|
60
|
+
if(formData != nil)
|
|
61
|
+
{
|
|
62
|
+
[postData appendData:formData];
|
|
63
|
+
// close form data
|
|
64
|
+
[postData appendData: [[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
65
|
+
[request setHTTPBody:postData];
|
|
66
|
+
}
|
|
67
|
+
// set content-length
|
|
68
|
+
[mheaders setValue:[NSString stringWithFormat:@"%lu",[postData length]] forKey:@"Content-Length"];
|
|
69
|
+
[mheaders setValue:@"100-continue" forKey:@"Expect"];
|
|
70
|
+
// appaned boundary to content-type
|
|
71
|
+
[mheaders setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forKey:@"content-type"];
|
|
72
|
+
[request setHTTPMethod: method];
|
|
73
|
+
[request setAllHTTPHeaderFields:mheaders];
|
|
74
|
+
onComplete(request, [formData length]);
|
|
75
|
+
}
|
|
76
|
+
}];
|
|
77
|
+
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Fetch blob data request
|
|
82
|
+
+(void) buildOctetRequest:(NSDictionary *)options
|
|
83
|
+
taskId:(NSString *)taskId
|
|
84
|
+
method:(NSString *)method
|
|
85
|
+
url:(NSString *)url
|
|
86
|
+
headers:(NSDictionary *)headers
|
|
87
|
+
body:(NSString *)body
|
|
88
|
+
onComplete:(void(^)(__weak NSURLRequest * req, long bodyLength))onComplete
|
|
89
|
+
{
|
|
90
|
+
// NSString * encodedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
91
|
+
NSString * encodedUrl = url;
|
|
92
|
+
// send request
|
|
93
|
+
__block NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
|
|
94
|
+
initWithURL:[NSURL URLWithString: encodedUrl]];
|
|
95
|
+
|
|
96
|
+
__block NSMutableDictionary *mheaders = [[NSMutableDictionary alloc] initWithDictionary:[ReactNativeBlobUtilNetwork normalizeHeaders:headers]];
|
|
97
|
+
// move heavy task to another thread
|
|
98
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
99
|
+
NSMutableData * blobData;
|
|
100
|
+
long size = -1;
|
|
101
|
+
// if method is POST, PUT or PATCH, convert data string format
|
|
102
|
+
if([[method lowercaseString] isEqualToString:@"post"] || [[method lowercaseString] isEqualToString:@"put"] || [[method lowercaseString] isEqualToString:@"patch"]) {
|
|
103
|
+
// generate octet-stream body
|
|
104
|
+
if(body != nil) {
|
|
105
|
+
__block NSString * cType = [[self class] getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
|
|
106
|
+
__block NSString * transferEncoding = [[self class] getHeaderIgnoreCases:@"transfer-encoding" fromHeaders:mheaders];
|
|
107
|
+
// when headers does not contain a key named "content-type" (case ignored), use default content type
|
|
108
|
+
if(cType == nil)
|
|
109
|
+
{
|
|
110
|
+
[mheaders setValue:@"application/octet-stream" forKey:@"Content-Type"];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// when body is a string contains file path prefix, try load file from the path
|
|
114
|
+
if([body hasPrefix:FILE_PREFIX]) {
|
|
115
|
+
__block NSString * orgPath = [body substringFromIndex:[FILE_PREFIX length]];
|
|
116
|
+
orgPath = [ReactNativeBlobUtilFS getPathOfAsset:orgPath];
|
|
117
|
+
orgPath = [[NSURL URLWithString:orgPath] path];
|
|
118
|
+
if([orgPath hasPrefix:AL_PREFIX])
|
|
119
|
+
{
|
|
120
|
+
|
|
121
|
+
[ReactNativeBlobUtilFS readFile:orgPath encoding:nil transformFile:false onComplete:^(id content, NSString* code, NSString * err) {
|
|
122
|
+
if(err != nil)
|
|
123
|
+
{
|
|
124
|
+
onComplete(nil, 0);
|
|
125
|
+
}
|
|
126
|
+
else
|
|
127
|
+
{
|
|
128
|
+
[request setHTTPBody:((NSData *)content)];
|
|
129
|
+
[request setHTTPMethod: method];
|
|
130
|
+
[request setAllHTTPHeaderFields:mheaders];
|
|
131
|
+
onComplete(request, [((NSData *)content) length]);
|
|
132
|
+
}
|
|
133
|
+
}];
|
|
134
|
+
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
size = [[[NSFileManager defaultManager] attributesOfItemAtPath:orgPath error:nil] fileSize];
|
|
138
|
+
if(transferEncoding != nil && [[transferEncoding lowercaseString] isEqualToString:@"chunked"])
|
|
139
|
+
{
|
|
140
|
+
[request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
|
|
141
|
+
}
|
|
142
|
+
else
|
|
143
|
+
{
|
|
144
|
+
__block NSData * bodyBytes = [NSData dataWithContentsOfFile:orgPath ];
|
|
145
|
+
[request setHTTPBody:bodyBytes];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// otherwise convert it as BASE64 data string
|
|
149
|
+
else {
|
|
150
|
+
|
|
151
|
+
__block NSString * cType = [[self class]getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
|
|
152
|
+
// when content-type is application/octet* decode body string using BASE64 decoder
|
|
153
|
+
if([[cType lowercaseString] hasPrefix:@"application/octet"] || [[cType lowercaseString] containsString:@";base64"])
|
|
154
|
+
{
|
|
155
|
+
__block NSString * ncType = [[cType stringByReplacingOccurrencesOfString:@";base64" withString:@""]stringByReplacingOccurrencesOfString:@";BASE64" withString:@""];
|
|
156
|
+
if([mheaders valueForKey:@"content-type"] != nil)
|
|
157
|
+
[mheaders setValue:ncType forKey:@"content-type"];
|
|
158
|
+
if([mheaders valueForKey:@"Content-Type"] != nil)
|
|
159
|
+
[mheaders setValue:ncType forKey:@"Content-Type"];
|
|
160
|
+
blobData = [[NSData alloc] initWithBase64EncodedString:body options:0];
|
|
161
|
+
[request setHTTPBody:blobData];
|
|
162
|
+
size = [blobData length];
|
|
163
|
+
}
|
|
164
|
+
// otherwise use the body as-is
|
|
165
|
+
else
|
|
166
|
+
{
|
|
167
|
+
size = [body length];
|
|
168
|
+
[request setHTTPBody: [body dataUsingEncoding:NSUTF8StringEncoding]];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
[request setHTTPMethod: method];
|
|
175
|
+
[request setAllHTTPHeaderFields:mheaders];
|
|
176
|
+
|
|
177
|
+
onComplete(request, size);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
+(void) buildFormBody:(NSArray *)form boundary:(NSString *)boundary onComplete:(void(^)(NSData * formData, BOOL hasError))onComplete
|
|
182
|
+
{
|
|
183
|
+
__block NSMutableData * formData = [[NSMutableData alloc] init];
|
|
184
|
+
if(form == nil)
|
|
185
|
+
onComplete(nil, NO);
|
|
186
|
+
else
|
|
187
|
+
{
|
|
188
|
+
__block int i = 0;
|
|
189
|
+
__block int count = [form count];
|
|
190
|
+
// a recursive block that builds multipart body asynchornously
|
|
191
|
+
void __block (^getFieldData)(id field) = ^(id field)
|
|
192
|
+
{
|
|
193
|
+
NSString * name = [field valueForKey:@"name"];
|
|
194
|
+
__block NSString * content = [field valueForKey:@"data"];
|
|
195
|
+
NSString * contentType = [field valueForKey:@"type"];
|
|
196
|
+
// skip when the form field `name` or `data` is empty
|
|
197
|
+
if(content == nil || name == nil)
|
|
198
|
+
{
|
|
199
|
+
i++;
|
|
200
|
+
getFieldData([form objectAtIndex:i]);
|
|
201
|
+
RCTLogWarn(@"ReactNativeBlobUtil multipart request builder has found a field without `data` or `name` property, the field will be removed implicitly.");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// field is a text field
|
|
206
|
+
if([field valueForKey:@"filename"] == nil || content == nil) {
|
|
207
|
+
contentType = contentType == nil ? @"text/plain" : contentType;
|
|
208
|
+
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
209
|
+
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", name] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
210
|
+
[formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
211
|
+
[formData appendData:[[NSString stringWithFormat:@"%@\r\n", content] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
212
|
+
}
|
|
213
|
+
// field contains a file
|
|
214
|
+
else {
|
|
215
|
+
contentType = contentType == nil ? @"application/octet-stream" : contentType;
|
|
216
|
+
NSMutableData * blobData;
|
|
217
|
+
if(content != nil)
|
|
218
|
+
{
|
|
219
|
+
// append data from file asynchronously
|
|
220
|
+
if([content hasPrefix:FILE_PREFIX])
|
|
221
|
+
{
|
|
222
|
+
NSString * orgPath = [content substringFromIndex:[FILE_PREFIX length]];
|
|
223
|
+
orgPath = [ReactNativeBlobUtilFS getPathOfAsset:orgPath];
|
|
224
|
+
|
|
225
|
+
[ReactNativeBlobUtilFS readFile:orgPath encoding:nil transformFile:false onComplete:^(NSData *content, NSString* code, NSString * err) {
|
|
226
|
+
if(err != nil)
|
|
227
|
+
{
|
|
228
|
+
onComplete(formData, YES);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
NSString * filename = [field valueForKey:@"filename"];
|
|
232
|
+
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
233
|
+
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
234
|
+
[formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
235
|
+
[formData appendData:content];
|
|
236
|
+
[formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
237
|
+
i++;
|
|
238
|
+
if(i < count)
|
|
239
|
+
{
|
|
240
|
+
__block NSDictionary * nextField = [form objectAtIndex:i];
|
|
241
|
+
getFieldData(nextField);
|
|
242
|
+
}
|
|
243
|
+
else
|
|
244
|
+
{
|
|
245
|
+
onComplete(formData, NO);
|
|
246
|
+
getFieldData = nil;
|
|
247
|
+
}
|
|
248
|
+
}];
|
|
249
|
+
return ;
|
|
250
|
+
}
|
|
251
|
+
else
|
|
252
|
+
blobData = [[NSData alloc] initWithBase64EncodedString:content options:0];
|
|
253
|
+
}
|
|
254
|
+
NSString * filename = [field valueForKey:@"filename"];
|
|
255
|
+
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
256
|
+
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
257
|
+
[formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
258
|
+
[formData appendData:blobData];
|
|
259
|
+
[formData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
260
|
+
blobData = nil;
|
|
261
|
+
}
|
|
262
|
+
i++;
|
|
263
|
+
if(i < count)
|
|
264
|
+
{
|
|
265
|
+
__block NSDictionary * nextField = [form objectAtIndex:i];
|
|
266
|
+
getFieldData(nextField);
|
|
267
|
+
}
|
|
268
|
+
else
|
|
269
|
+
{
|
|
270
|
+
onComplete(formData, NO);
|
|
271
|
+
getFieldData = nil;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
};
|
|
275
|
+
__block NSDictionary * nextField = [form objectAtIndex:i];
|
|
276
|
+
getFieldData(nextField);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers {
|
|
281
|
+
|
|
282
|
+
NSString * normalCase = [headers valueForKey:field];
|
|
283
|
+
NSString * ignoredCase = [headers valueForKey:[field lowercaseString]];
|
|
284
|
+
if( normalCase != nil)
|
|
285
|
+
return normalCase;
|
|
286
|
+
else
|
|
287
|
+
return ignoredCase;
|
|
288
|
+
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@end
|