diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java index 9a7d34a..52716b5 100644 --- a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java +++ b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java @@ -163,12 +163,17 @@ public class CordovaHttpPlugin extends CordovaPlugin implements Observer { int readTimeout = args.getInt(5) * 1000; boolean followRedirect = args.getBoolean(6); String responseType = args.getString(7); - Integer reqId = args.getInt(8); + JSONObject transmitOptions = args.getJSONObject(8); + Integer reqId = args.getInt(9); + + // new file transmission options + String transmitFileType = transmitOptions.getString("transmitFileAs"); + boolean submitRaw = transmitFileType.equalsIgnoreCase("BINARY"); CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId); CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, connectTimeout, readTimeout, followRedirect, - responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), observableCallbackContext); + responseType, this.tlsConfiguration, submitRaw, this.cordova.getActivity().getApplicationContext(), observableCallbackContext); startRequest(reqId, observableCallbackContext, upload); diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java index d37464d..ef74c1a 100644 --- a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java +++ b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java @@ -25,18 +25,40 @@ class CordovaHttpUpload extends CordovaHttpBase { private JSONArray uploadNames; private Context applicationContext; + private boolean submitRaw = false; + public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int connectTimeout, int readTimeout, - boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration, + boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration, boolean submitRaw, Context applicationContext, CordovaObservableCallbackContext callbackContext) { super("POST", url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext); this.filePaths = filePaths; this.uploadNames = uploadNames; this.applicationContext = applicationContext; + this.submitRaw = submitRaw; } @Override protected void sendBody(HttpRequest request) throws Exception { + if (this.submitRaw) { + if (this.filePaths.length() != 1) { + throw new IllegalArgumentException("Can only transmit a single file. Multiple files are not supported in this mode."); + } + + String filePath = this.filePaths.getString(0); + Uri fileURI = Uri.parse(filePath); + + if (ContentResolver.SCHEME_FILE.equals((fileURI.getScheme()))) { + File file = new File(new URI(filePath)); + request.send(file); + } else if (ContentResolver.SCHEME_CONTENT.equals(fileURI.getScheme())) { + InputStream inputStream = this.applicationContext.getContentResolver().openInputStream(fileURI); + request.send(inputStream); + } + + return; + } + for (int i = 0; i < this.filePaths.length(); ++i) { String uploadName = this.uploadNames.getString(i); String filePath = this.filePaths.getString(i); diff --git a/src/ios/CordovaHttpPlugin.m b/src/ios/CordovaHttpPlugin.m index 97c98e0..58699d4 100644 --- a/src/ios/CordovaHttpPlugin.m +++ b/src/ios/CordovaHttpPlugin.m @@ -460,50 +460,30 @@ NSDictionary *headers = [command.arguments objectAtIndex:1]; NSArray *filePaths = [command.arguments objectAtIndex: 2]; NSArray *names = [command.arguments objectAtIndex: 3]; - NSTimeInterval connectTimeout = [[command.arguments objectAtIndex:4] doubleValue]; + NSTimeInterval _connectTimeout = [[command.arguments objectAtIndex:4] doubleValue]; NSTimeInterval readTimeout = [[command.arguments objectAtIndex:5] doubleValue]; bool followRedirect = [[command.arguments objectAtIndex:6] boolValue]; NSString *responseType = [command.arguments objectAtIndex:7]; - NSNumber *reqId = [command.arguments objectAtIndex:8]; - - [self setRequestHeaders: headers forManager: manager]; - [self setTimeout:readTimeout forManager:manager]; - [self setRedirect:followRedirect forManager:manager]; - [self setResponseSerializer:responseType forManager:manager]; + NSDictionary *transmitOptions = [command.arguments objectAtIndex:8]; + NSNumber *reqId = [command.arguments objectAtIndex:9]; + + NSString *transmitFileType = [transmitOptions objectForKey:@"transmitFileAs"]; CordovaHttpPlugin* __weak weakSelf = self; [[SDNetworkActivityIndicator sharedActivityIndicator] startActivity]; - @try { - NSURLSessionDataTask *task = [manager POST:url parameters:nil constructingBodyWithBlock:^(id formData) { - NSError *error; - for (int i = 0; i < [filePaths count]; i++) { - NSString *filePath = (NSString *) [filePaths objectAtIndex:i]; - NSString *uploadName = (NSString *) [names objectAtIndex:i]; - NSURL *fileURL = [NSURL URLWithString: filePath]; - [formData appendPartWithFileURL:fileURL name:uploadName error:&error]; - } - if (error) { - [weakSelf removeRequest:reqId]; - - NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; - [dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"]; - [dictionary setObject:@"Could not add file to post body." forKey:@"error"]; - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; - [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; - return; - } - } progress:nil success:^(NSURLSessionTask *task, id responseObject) { - [weakSelf removeRequest:reqId]; - - NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; - [self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject]; - - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; - [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; - } failure:^(NSURLSessionTask *task, NSError *error) { + @try + { + NSURLSessionDataTask* task; + + void (^setupManager)(void) = ^() { + [self setRequestHeaders: headers forManager: manager]; + [self setTimeout:readTimeout forManager:manager]; + [self setRedirect:followRedirect forManager:manager]; + [self setResponseSerializer:responseType forManager:manager]; + }; + + void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) { [weakSelf removeRequest:reqId]; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; @@ -512,10 +492,95 @@ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; - }]; + [manager invalidateSessionCancelingTasks:YES]; + }; + + void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) { + [weakSelf removeRequest:reqId]; + + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject]; + + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; + [manager invalidateSessionCancelingTasks:YES]; + }; + + bool submitRaw = [transmitFileType isEqualToString:@"BINARY"]; + + // RAW + if (submitRaw) + { + if ([filePaths count] == 1) + { + // setup the request serializer to submit the raw file content + manager.requestSerializer = [BinaryRequestSerializer serializer]; + setupManager(); + + NSURL *fileURL = [NSURL URLWithString:[filePaths objectAtIndex:0]]; + NSError *error; + NSData *fileData = [NSData dataWithContentsOfURL:fileURL options:0 error:&error]; + + if (error) + { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [self handleError:dictionary withResponse:nil error:error]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; + [manager invalidateSessionCancelingTasks:YES]; + return; + } + + task = [manager uploadTaskWithHTTPMethod:@"POST" URLString:url parameters:fileData progress:nil success:onSuccess failure:onFailure]; + } + else + { + [NSException raise:@"ArgumentException" format:@"Can only transmit a single file. Multiple files are not supported in this mode."]; + } + } + else // FALLBACK: Multipart / FormData + { + setupManager(); + task = [manager + POST:url + parameters:nil + constructingBodyWithBlock:^(id formData) + { + NSError *error; + + for (int i = 0; i < [filePaths count]; i++) + { + NSString *filePath = (NSString *) [filePaths objectAtIndex:i]; + NSString *uploadName = (NSString *) [names objectAtIndex:i]; + NSURL *fileURL = [NSURL URLWithString: filePath]; + [formData appendPartWithFileURL:fileURL name:uploadName error:&error]; + } + + if (error) + { + [weakSelf removeRequest:reqId]; + + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"]; + [dictionary setObject:@"Could not add file to post body." forKey:@"error"]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; + return; + } + } + progress:nil + success:onSuccess + failure:onFailure + ]; + } + [self addRequest:reqId forTask:task]; } - @catch (NSException *exception) { + @catch (NSException *exception) + { [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; [self handleException:exception withCommand:command]; } diff --git a/www/public-interface.js b/www/public-interface.js index 66b5261..15fd51b 100644 --- a/www/public-interface.js +++ b/www/public-interface.js @@ -180,7 +180,13 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf break; case 'upload': var fileOptions = helpers.checkUploadFileOptions(options.filePath, options.name); - exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, reqId]); + + // support uploading files as octet-stream / encoded string instead of form-data + var transmitOptions = {}; + transmitOptions.transmitFileAs = options.transmitFileAs || 'FORMDATA'; + // transmitOptions.transmitMethod = options.transmitMethod || 'POST'; + + exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, transmitOptions, reqId]); break; case 'download': var filePath = helpers.checkDownloadFilePath(options.filePath);