Merge branch 'CB-6503'
diff --git a/README.md b/README.md
index 7605ce3..4ee0774 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,7 @@
 # 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 @@
   - __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 @@
 
                     // 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 @@
                     
                     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 @@
                             }
                             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 @@
                             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 @@
 
                     // 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 @@
                     
                     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 @@
                             }
                             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 @@
                             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 @@
         [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 @@
     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 @@
 
         [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 @@
             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;
 }