diff --git a/README.md b/README.md index 7605ce3..4ee0774 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Although in the global scope, they are not available until after the `deviceread # FileTransfer The `FileTransfer` object provides a way to upload files using an HTTP -multi-part POST request, and to download files as well. +multi-part POST or PUT request, and to download files as well. ## Properties @@ -91,8 +91,9 @@ __Parameters__: - __mimeType__: The mime type of the data to upload. Defaults to `image/jpeg`. (DOMString) - __params__: A set of optional key/value pairs to pass in the HTTP request. (Object) - __chunkedMode__: Whether to upload the data in chunked streaming mode. Defaults to `true`. (Boolean) - - __headers__: A map of header name/header values. Use an array to specify more than one value. (Object) - + - __headers__: A map of header name/header values. Use an array to specify more than one value. On iOS, FireOS, and Android, if a header named Content-Type is present, multipart form data will NOT be used. (Object) + - __httpMethod__: The HTTP method to use e.g. POST or PUT. Defaults to `POST`. (DOMString) + - __trustAllHosts__: Optional parameter, defaults to `false`. If set to `true`, it accepts all security certificates. This is useful since Android rejects self-signed security certificates. Not recommended for production use. Supported on Android and iOS. _(boolean)_ ### Example diff --git a/src/amazon/FileTransfer.java b/src/amazon/FileTransfer.java index 513400e..1563a39 100644 --- a/src/amazon/FileTransfer.java +++ b/src/amazon/FileTransfer.java @@ -315,8 +315,12 @@ public class FileTransfer extends CordovaPlugin { // Use a post method. conn.setRequestMethod(httpMethod); - conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); + // if we specified a Content-Type header, don't do multipart form upload + boolean multipartFormUpload = (headers == null) || !headers.has("Content-Type"); + if (multipartFormUpload) { + conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); + } // Set the cookies on the response String cookie = AmazonCookieManager.getInstance().getCookie(target); if (cookie != null) { @@ -362,7 +366,9 @@ public class FileTransfer extends CordovaPlugin { int stringLength = beforeDataBytes.length + tailParamsBytes.length; if (readResult.length >= 0) { - fixedLength = (int)readResult.length + stringLength; + fixedLength = (int)readResult.length; + if (multipartFormUpload) + fixedLength += stringLength; progress.setLengthComputable(true); progress.setTotal(fixedLength); } @@ -393,10 +399,12 @@ public class FileTransfer extends CordovaPlugin { } context.currentOutputStream = sendStream; } - //We don't want to change encoding, we just want this to write for all Unicode. - sendStream.write(beforeDataBytes); - totalBytes += beforeDataBytes.length; - + + if (multipartFormUpload) { + //We don't want to change encoding, we just want this to write for all Unicode. + sendStream.write(beforeDataBytes); + totalBytes += beforeDataBytes.length; + } // create a buffer of maximum size int bytesAvailable = readResult.inputStream.available(); int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE); @@ -425,9 +433,11 @@ public class FileTransfer extends CordovaPlugin { context.sendPluginResult(progressResult); } - // send multipart form data necessary after file data... - sendStream.write(tailParamsBytes); - totalBytes += tailParamsBytes.length; + if (multipartFormUpload) { + // send multipart form data necessary after file data... + sendStream.write(tailParamsBytes); + totalBytes += tailParamsBytes.length; + } sendStream.flush(); } finally { safeClose(readResult.inputStream); diff --git a/src/android/FileTransfer.java b/src/android/FileTransfer.java index 882bf3f..9c8dacb 100644 --- a/src/android/FileTransfer.java +++ b/src/android/FileTransfer.java @@ -341,8 +341,13 @@ public class FileTransfer extends CordovaPlugin { // Use a post method. conn.setRequestMethod(httpMethod); - conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); - + + // if we specified a Content-Type header, don't do multipart form upload + boolean multipartFormUpload = (headers == null) || !headers.has("Content-Type"); + if (multipartFormUpload) { + conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); + } + // Set the cookies on the response String cookie = getCookies(target); @@ -389,7 +394,9 @@ public class FileTransfer extends CordovaPlugin { int stringLength = beforeDataBytes.length + tailParamsBytes.length; if (readResult.length >= 0) { - fixedLength = (int)readResult.length + stringLength; + fixedLength = (int)readResult.length; + if (multipartFormUpload) + fixedLength += stringLength; progress.setLengthComputable(true); progress.setTotal(fixedLength); } @@ -420,10 +427,13 @@ public class FileTransfer extends CordovaPlugin { } context.connection = conn; } - //We don't want to change encoding, we just want this to write for all Unicode. - sendStream.write(beforeDataBytes); - totalBytes += beforeDataBytes.length; - + + if (multipartFormUpload) { + //We don't want to change encoding, we just want this to write for all Unicode. + sendStream.write(beforeDataBytes); + totalBytes += beforeDataBytes.length; + } + // create a buffer of maximum size int bytesAvailable = readResult.inputStream.available(); int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE); @@ -452,9 +462,11 @@ public class FileTransfer extends CordovaPlugin { context.sendPluginResult(progressResult); } - // send multipart form data necessary after file data... - sendStream.write(tailParamsBytes); - totalBytes += tailParamsBytes.length; + if (multipartFormUpload) { + // send multipart form data necessary after file data... + sendStream.write(tailParamsBytes); + totalBytes += tailParamsBytes.length; + } sendStream.flush(); } finally { safeClose(readResult.inputStream); diff --git a/src/ios/CDVFileTransfer.m b/src/ios/CDVFileTransfer.m index 3031159..70ddb35 100644 --- a/src/ios/CDVFileTransfer.m +++ b/src/ios/CDVFileTransfer.m @@ -183,8 +183,12 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) [req setHTTPShouldHandleCookies:NO]; } - NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kFormBoundary]; - [req setValue:contentType forHTTPHeaderField:@"Content-Type"]; + // if we specified a Content-Type header, don't do multipart form upload + BOOL multipartFormUpload = [headers objectForKey:@"Content-Type"] == nil; + if (multipartFormUpload) { + NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kFormBoundary]; + [req setValue:contentType forHTTPHeaderField:@"Content-Type"]; + } [self applyRequestHeaders:headers toRequest:req]; NSData* formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding]; @@ -220,7 +224,11 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) DLog(@"fileData length: %d", [fileData length]); NSData* postBodyAfterFile = [[NSString stringWithFormat:@"\r\n--%@--\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding]; - long long totalPayloadLength = [postBodyBeforeFile length] + [fileData length] + [postBodyAfterFile length]; + long long totalPayloadLength = [fileData length]; + if (multipartFormUpload) { + totalPayloadLength += [postBodyBeforeFile length] + [postBodyAfterFile length]; + } + [req setValue:[[NSNumber numberWithLongLong:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"]; if (chunkedMode) { @@ -231,14 +239,18 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) [self.commandDelegate runInBackground:^{ if (CFWriteStreamOpen(writeStream)) { - NSData* chunks[] = {postBodyBeforeFile, fileData, postBodyAfterFile}; - int numChunks = sizeof(chunks) / sizeof(chunks[0]); + if (multipartFormUpload) { + NSData* chunks[] = { postBodyBeforeFile, fileData, postBodyAfterFile }; + int numChunks = sizeof(chunks) / sizeof(chunks[0]); - for (int i = 0; i < numChunks; ++i) { - CFIndex result = WriteDataToStream(chunks[i], writeStream); - if (result <= 0) { - break; + for (int i = 0; i < numChunks; ++i) { + CFIndex result = WriteDataToStream(chunks[i], writeStream); + if (result <= 0) { + break; + } } + } else { + WriteDataToStream(fileData, writeStream); } } else { NSLog(@"FileTransfer: Failed to open writeStream"); @@ -247,9 +259,13 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) CFRelease(writeStream); }]; } else { - [postBodyBeforeFile appendData:fileData]; - [postBodyBeforeFile appendData:postBodyAfterFile]; - [req setHTTPBody:postBodyBeforeFile]; + if (multipartFormUpload) { + [postBodyBeforeFile appendData:fileData]; + [postBodyBeforeFile appendData:postBodyAfterFile]; + [req setHTTPBody:postBodyBeforeFile]; + } else { + [req setHTTPBody:fileData]; + } } return req; }