Skip to content

Commit 5e88d2e

Browse files
deenelh4ck-rOOtsilkimen
authored
H4ck r o ot upload raw file2 (#2)
* feat: upload file without formdata * fix: handle 'missing' options * test: implement tests for silkimen#495 * chore: some cleanup * add fork version, add transmitfileas to readme * bump version --------- Co-authored-by: Tobias Becht <[email protected]> Co-authored-by: Sefa Ilkimen <[email protected]>
1 parent c34fb61 commit 5e88d2e

File tree

8 files changed

+168
-42
lines changed

8 files changed

+168
-42
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ The options object contains following keys:
266266
* `headers`: headers object (key value pair), will be merged with global values
267267
* `filePath`: file path(s) to be used during upload and download see [uploadFile](#uploadFile) and [downloadFile](#downloadFile) for detailed information
268268
* `name`: name(s) to be used during upload see [uploadFile](#uploadFile) for detailed information
269+
* `transmitFileAs`: option for transmitting files in specific format. supports "BINARY", NULL, undefined or any string except "binary" for now. you can use http.sendRequest({ method: 'upload', filePath: '<path>', name: '<name>', transmitFileAs: 'BINARY', url: '<your url>' })
269270

270271
Here's a quick example:
271272

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@mobisys-internal/cordova-plugin-advanced-http",
3-
"version": "3.3.1",
3+
"version": "3.3.2",
44
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
55
"scripts": {
66
"update:cert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",

src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,17 @@ private boolean uploadFiles(final JSONArray args, final CallbackContext callback
163163
int readTimeout = args.getInt(5) * 1000;
164164
boolean followRedirect = args.getBoolean(6);
165165
String responseType = args.getString(7);
166-
Integer reqId = args.getInt(8);
166+
JSONObject transmitOptions = args.getJSONObject(8);
167+
Integer reqId = args.getInt(9);
168+
169+
// new file transmission options
170+
String transmitFileType = transmitOptions.getString("transmitFileAs");
171+
boolean submitRaw = transmitFileType.equalsIgnoreCase("BINARY");
167172

168173
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
169174

170175
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, connectTimeout, readTimeout, followRedirect,
171-
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), observableCallbackContext);
176+
responseType, this.tlsConfiguration, submitRaw, this.cordova.getActivity().getApplicationContext(), observableCallbackContext);
172177

173178
startRequest(reqId, observableCallbackContext, upload);
174179

src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,40 @@ class CordovaHttpUpload extends CordovaHttpBase {
2525
private JSONArray uploadNames;
2626
private Context applicationContext;
2727

28+
private boolean submitRaw = false;
29+
2830
public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int connectTimeout, int readTimeout,
29-
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
31+
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration, boolean submitRaw,
3032
Context applicationContext, CordovaObservableCallbackContext callbackContext) {
3133

3234
super("POST", url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
3335
this.filePaths = filePaths;
3436
this.uploadNames = uploadNames;
3537
this.applicationContext = applicationContext;
38+
this.submitRaw = submitRaw;
3639
}
3740

3841
@Override
3942
protected void sendBody(HttpRequest request) throws Exception {
43+
if (this.submitRaw) {
44+
if (this.filePaths.length() != 1) {
45+
throw new IllegalArgumentException("Can only transmit a single file. Multiple files are not supported in this mode.");
46+
}
47+
48+
String filePath = this.filePaths.getString(0);
49+
Uri fileURI = Uri.parse(filePath);
50+
51+
if (ContentResolver.SCHEME_FILE.equals((fileURI.getScheme()))) {
52+
File file = new File(new URI(filePath));
53+
request.send(file);
54+
} else if (ContentResolver.SCHEME_CONTENT.equals(fileURI.getScheme())) {
55+
InputStream inputStream = this.applicationContext.getContentResolver().openInputStream(fileURI);
56+
request.send(inputStream);
57+
}
58+
59+
return;
60+
}
61+
4062
for (int i = 0; i < this.filePaths.length(); ++i) {
4163
String uploadName = this.uploadNames.getString(i);
4264
String filePath = this.filePaths.getString(i);

src/ios/CordovaHttpPlugin.m

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -460,62 +460,127 @@ - (void)uploadFiles:(CDVInvokedUrlCommand*)command {
460460
NSDictionary *headers = [command.arguments objectAtIndex:1];
461461
NSArray *filePaths = [command.arguments objectAtIndex: 2];
462462
NSArray *names = [command.arguments objectAtIndex: 3];
463-
NSTimeInterval connectTimeout = [[command.arguments objectAtIndex:4] doubleValue];
463+
NSTimeInterval _connectTimeout = [[command.arguments objectAtIndex:4] doubleValue];
464464
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:5] doubleValue];
465465
bool followRedirect = [[command.arguments objectAtIndex:6] boolValue];
466466
NSString *responseType = [command.arguments objectAtIndex:7];
467-
NSNumber *reqId = [command.arguments objectAtIndex:8];
468-
469-
[self setRequestHeaders: headers forManager: manager];
470-
[self setTimeout:readTimeout forManager:manager];
471-
[self setRedirect:followRedirect forManager:manager];
472-
[self setResponseSerializer:responseType forManager:manager];
467+
NSDictionary *transmitOptions = [command.arguments objectAtIndex:8];
468+
NSNumber *reqId = [command.arguments objectAtIndex:9];
469+
470+
NSString *transmitFileType = [transmitOptions objectForKey:@"transmitFileAs"];
473471

474472
CordovaHttpPlugin* __weak weakSelf = self;
475473
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
476474

477-
@try {
478-
NSURLSessionDataTask *task = [manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
479-
NSError *error;
480-
for (int i = 0; i < [filePaths count]; i++) {
481-
NSString *filePath = (NSString *) [filePaths objectAtIndex:i];
482-
NSString *uploadName = (NSString *) [names objectAtIndex:i];
483-
NSURL *fileURL = [NSURL URLWithString: filePath];
484-
[formData appendPartWithFileURL:fileURL name:uploadName error:&error];
485-
}
486-
if (error) {
487-
[weakSelf removeRequest:reqId];
488-
489-
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
490-
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
491-
[dictionary setObject:@"Could not add file to post body." forKey:@"error"];
492-
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
493-
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
494-
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
495-
return;
496-
}
497-
} progress:nil success:^(NSURLSessionTask *task, id responseObject) {
475+
@try
476+
{
477+
NSURLSessionDataTask* task;
478+
479+
void (^setupManager)(void) = ^() {
480+
[self setRequestHeaders: headers forManager: manager];
481+
[self setTimeout:readTimeout forManager:manager];
482+
[self setRedirect:followRedirect forManager:manager];
483+
[self setResponseSerializer:responseType forManager:manager];
484+
};
485+
486+
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
498487
[weakSelf removeRequest:reqId];
499488

500489
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
501-
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
490+
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
502491

503-
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
492+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
504493
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
505494
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
506-
} failure:^(NSURLSessionTask *task, NSError *error) {
495+
[manager invalidateSessionCancelingTasks:YES];
496+
};
497+
498+
void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) {
507499
[weakSelf removeRequest:reqId];
508500

509501
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
510-
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
502+
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
511503

512-
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
504+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
513505
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
514506
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
515-
}];
507+
[manager invalidateSessionCancelingTasks:YES];
508+
};
509+
510+
bool submitRaw = [transmitFileType isEqualToString:@"BINARY"];
511+
512+
// RAW
513+
if (submitRaw)
514+
{
515+
if ([filePaths count] == 1)
516+
{
517+
// setup the request serializer to submit the raw file content
518+
manager.requestSerializer = [BinaryRequestSerializer serializer];
519+
setupManager();
520+
521+
NSURL *fileURL = [NSURL URLWithString:[filePaths objectAtIndex:0]];
522+
NSError *error;
523+
NSData *fileData = [NSData dataWithContentsOfURL:fileURL options:0 error:&error];
524+
525+
if (error)
526+
{
527+
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
528+
[self handleError:dictionary withResponse:nil error:error];
529+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
530+
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
531+
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
532+
[manager invalidateSessionCancelingTasks:YES];
533+
return;
534+
}
535+
536+
task = [manager uploadTaskWithHTTPMethod:@"POST" URLString:url parameters:fileData progress:nil success:onSuccess failure:onFailure];
537+
}
538+
else
539+
{
540+
[NSException raise:@"ArgumentException" format:@"Can only transmit a single file. Multiple files are not supported in this mode."];
541+
}
542+
}
543+
else // FALLBACK: Multipart / FormData
544+
{
545+
setupManager();
546+
task = [manager
547+
POST:url
548+
parameters:nil
549+
constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
550+
{
551+
NSError *error;
552+
553+
for (int i = 0; i < [filePaths count]; i++)
554+
{
555+
NSString *filePath = (NSString *) [filePaths objectAtIndex:i];
556+
NSString *uploadName = (NSString *) [names objectAtIndex:i];
557+
NSURL *fileURL = [NSURL URLWithString: filePath];
558+
[formData appendPartWithFileURL:fileURL name:uploadName error:&error];
559+
}
560+
561+
if (error)
562+
{
563+
[weakSelf removeRequest:reqId];
564+
565+
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
566+
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
567+
[dictionary setObject:@"Could not add file to post body." forKey:@"error"];
568+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
569+
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
570+
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
571+
return;
572+
}
573+
}
574+
progress:nil
575+
success:onSuccess
576+
failure:onFailure
577+
];
578+
}
579+
516580
[self addRequest:reqId forTask:task];
517581
}
518-
@catch (NSException *exception) {
582+
@catch (NSException *exception)
583+
{
519584
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
520585
[self handleException:exception withCommand:command];
521586
}

test/e2e-specs.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,34 @@ const tests = [
11781178
result.data.should.be.eql({ status: -2, error: messageFactory.handshakeFailed() });
11791179
}
11801180
},
1181+
{
1182+
description: 'should upload a file in binary mode from given path in local filesystem to given URL #495',
1183+
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
1184+
func: function (resolve, reject) {
1185+
var fileName = 'test-file.txt';
1186+
var fileContent = 'I am a dummy file. I am used for testing purposes!';
1187+
var sourcePath = cordova.file.cacheDirectory + fileName;
1188+
var targetUrl = 'http://httpbin.org/post';
1189+
1190+
var options = { method: 'upload', transmitFileAs: 'BINARY', filePath: sourcePath, name: fileName };
1191+
1192+
helpers.writeToFile(function () {
1193+
cordova.plugin.http.sendRequest(targetUrl, options, resolve, reject);
1194+
}, fileName, fileContent);
1195+
},
1196+
validationFunc: function (driver, result) {
1197+
var fileName = 'test-file.txt';
1198+
var fileContent = 'I am a dummy file. I am used for testing purposes!';
1199+
1200+
result.type.should.be.equal('resolved');
1201+
result.data.data.should.be.a('string');
1202+
1203+
JSON
1204+
.parse(result.data.data)
1205+
.data
1206+
.should.be.equal(fileContent);
1207+
}
1208+
},
11811209
];
11821210

11831211
if (typeof module !== 'undefined' && module.exports) {

www/helpers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
498498
function handleMissingOptions(options, globals) {
499499
options = options || {};
500500

501-
return {
501+
return Object.assign({}, options, {
502502
data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data,
503503
filePath: options.filePath,
504504
followRedirect: checkFollowRedirectValue(options.followRedirect || globals.followRedirect),
@@ -511,6 +511,6 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
511511
connectTimeout: checkTimeoutValue(options.connectTimeout || globals.connectTimeout),
512512
readTimeout: checkTimeoutValue(options.readTimeout || globals.readTimeout),
513513
timeout: checkTimeoutValue(options.timeout || globals.timeout)
514-
};
514+
});
515515
}
516516
};

www/public-interface.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,12 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
180180
break;
181181
case 'upload':
182182
var fileOptions = helpers.checkUploadFileOptions(options.filePath, options.name);
183-
exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, reqId]);
183+
184+
// support uploading files as octet-stream / encoded string instead of form-data
185+
var transmitOptions = {};
186+
transmitOptions.transmitFileAs = options.transmitFileAs || 'FORMDATA';
187+
188+
exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, transmitOptions, reqId]);
184189
break;
185190
case 'download':
186191
var filePath = helpers.checkDownloadFilePath(options.filePath);

0 commit comments

Comments
 (0)