Repository: cordova-plugin-file-transfer
Updated Branches:
  refs/heads/master 9e93bad83 -> 007f98692


CB-9563 Mulptipart form data is used even a header named Content-Type is present

Adds non-multipart implementation for Windows and a corresponding test
Adds a fix for Android for uploadResult.bytesSent = 0 for small files
Fixes parameters of FILE_NOT_FOUND_ERR case in upload operation on Windows - 
passing source file name instead of duplicated server/destination argument

github: close #117


Project: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/commit/007f9869
Tree: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/tree/007f9869
Diff: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/diff/007f9869

Branch: refs/heads/master
Commit: 007f98692b15f5a6c4902abf393bee8dc5edeced
Parents: 9e93bad
Author: daserge <[email protected]>
Authored: Tue Nov 24 01:20:00 2015 +0300
Committer: daserge <[email protected]>
Committed: Wed Nov 25 19:57:48 2015 +0300

----------------------------------------------------------------------
 src/android/FileTransfer.java    |  59 +++++-----
 src/windows/FileTransferProxy.js | 215 ++++++++++++++++++----------------
 tests/tests.js                   |  40 +++++--
 3 files changed, 175 insertions(+), 139 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/007f9869/src/android/FileTransfer.java
----------------------------------------------------------------------
diff --git a/src/android/FileTransfer.java b/src/android/FileTransfer.java
index 0efbd4e..b9b99dc 100644
--- a/src/android/FileTransfer.java
+++ b/src/android/FileTransfer.java
@@ -291,7 +291,7 @@ public class FileTransfer extends CordovaPlugin {
         final JSONObject headers = args.optJSONObject(8) == null ? 
params.optJSONObject("headers") : args.optJSONObject(8);
         final String objectId = args.getString(9);
         final String httpMethod = getArgument(args, 10, "POST");
-        
+
         final CordovaResourceApi resourceApi = webView.getResourceApi();
 
         Log.d(LOG_TAG, "fileKey: " + fileKey);
@@ -303,7 +303,7 @@ public class FileTransfer extends CordovaPlugin {
         Log.d(LOG_TAG, "headers: " + headers);
         Log.d(LOG_TAG, "objectId: " + objectId);
         Log.d(LOG_TAG, "httpMethod: " + httpMethod);
-        
+
         final Uri targetUri = resourceApi.remapUri(Uri.parse(target));
         // Accept a path or a URI for the source.
         Uri tmpSrc = Uri.parse(source);
@@ -323,7 +323,7 @@ public class FileTransfer extends CordovaPlugin {
         synchronized (activeRequests) {
             activeRequests.put(objectId, context);
         }
-        
+
         cordova.getThreadPool().execute(new Runnable() {
             public void run() {
                 if (context.aborted) {
@@ -363,13 +363,13 @@ public class FileTransfer extends CordovaPlugin {
 
                     // Use a post method.
                     conn.setRequestMethod(httpMethod);
-                    
+
                     // 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);
 
@@ -410,10 +410,10 @@ public class FileTransfer extends CordovaPlugin {
                     byte[] beforeDataBytes = 
beforeData.toString().getBytes("UTF-8");
                     byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY 
+ LINE_START + LINE_END).getBytes("UTF-8");
 
-                    
+
                     // Get a input stream of the file on the phone
                     OpenForReadResult readResult = 
resourceApi.openForRead(sourceUri);
-                    
+
                     int stringLength = beforeDataBytes.length + 
tailParamsBytes.length;
                     if (readResult.length >= 0) {
                         fixedLength = (int)readResult.length;
@@ -439,7 +439,7 @@ public class FileTransfer extends CordovaPlugin {
                     }
 
                     conn.connect();
-                    
+
                     OutputStream sendStream = null;
                     try {
                         sendStream = conn.getOutputStream();
@@ -449,26 +449,26 @@ public class FileTransfer extends CordovaPlugin {
                             }
                             context.connection = conn;
                         }
-                        
+
                         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);
                         byte[] buffer = new byte[bufferSize];
-    
+
                         // read file and write it into form...
                         int bytesRead = readResult.inputStream.read(buffer, 0, 
bufferSize);
-    
+
                         long prevBytesRead = 0;
                         while (bytesRead > 0) {
+                            totalBytes += bytesRead;
                             result.setBytesSent(totalBytes);
                             sendStream.write(buffer, 0, bytesRead);
-                            totalBytes += bytesRead;
                             if (totalBytes > prevBytesRead + 102400) {
                                 prevBytesRead = totalBytes;
                                 Log.d(LOG_TAG, "Uploaded " + totalBytes + " of 
" + fixedLength + " bytes");
@@ -483,7 +483,7 @@ public class FileTransfer extends CordovaPlugin {
                             progressResult.setKeepCallback(true);
                             context.sendPluginResult(progressResult);
                         }
-    
+
                         if (multipartFormUpload) {
                             // send multipart form data necessary after file 
data...
                             sendStream.write(tailParamsBytes);
@@ -513,7 +513,7 @@ public class FileTransfer extends CordovaPlugin {
                             }
                             context.connection = conn;
                         }
-                        
+
                         ByteArrayOutputStream out = new 
ByteArrayOutputStream(Math.max(1024, conn.getContentLength()));
                         byte[] buffer = new byte[1024];
                         int bytesRead = 0;
@@ -528,10 +528,10 @@ public class FileTransfer extends CordovaPlugin {
                         }
                         safeClose(inStream);
                     }
-                    
+
                     Log.d(LOG_TAG, "got response from server");
                     Log.d(LOG_TAG, responseString.substring(0, Math.min(256, 
responseString.length())));
-                    
+
                     // send request and retrieve response
                     result.setResponseCode(responseCode);
                     result.setResponse(responseString);
@@ -568,7 +568,7 @@ public class FileTransfer extends CordovaPlugin {
                             https.setSSLSocketFactory(oldSocketFactory);
                         }
                     }
-                }                
+                }
             }
         });
     }
@@ -601,11 +601,11 @@ public class FileTransfer extends CordovaPlugin {
         public java.security.cert.X509Certificate[] getAcceptedIssuers() {
             return new java.security.cert.X509Certificate[] {};
         }
-        
+
         public void checkClientTrusted(X509Certificate[] chain,
                 String authType) throws CertificateException {
         }
-        
+
         public void checkServerTrusted(X509Certificate[] chain,
                 String authType) throws CertificateException {
         }
@@ -735,7 +735,7 @@ public class FileTransfer extends CordovaPlugin {
         final boolean trustEveryone = args.optBoolean(2);
         final String objectId = args.getString(3);
         final JSONObject headers = args.optJSONObject(4);
-        
+
         final Uri sourceUri = resourceApi.remapUri(Uri.parse(source));
         // Accept a path or a URI for the source.
         Uri tmpTarget = Uri.parse(target);
@@ -790,12 +790,12 @@ public class FileTransfer extends CordovaPlugin {
             return;
         }
 
-        
+
         final RequestContext context = new RequestContext(source, target, 
callbackContext);
         synchronized (activeRequests) {
             activeRequests.put(objectId, context);
         }
-        
+
         cordova.getThreadPool().execute(new Runnable() {
             public void run() {
                 if (context.aborted) {
@@ -815,7 +815,7 @@ public class FileTransfer extends CordovaPlugin {
 
                     file = resourceApi.mapUriToFile(targetUri);
                     context.targetFile = file;
-                    
+
                     Log.d(LOG_TAG, "Download file:" + sourceUri);
 
                     FileProgressResult progress = new FileProgressResult();
@@ -840,9 +840,9 @@ public class FileTransfer extends CordovaPlugin {
                             // Setup the connection not to verify hostnames
                             https.setHostnameVerifier(DO_NOT_VERIFY);
                         }
-        
+
                         connection.setRequestMethod("GET");
-        
+
                         // TODO: Make OkHttp use this CookieManager by default.
                         String cookie = getCookies(sourceUri.toString());
 
@@ -850,15 +850,15 @@ public class FileTransfer extends CordovaPlugin {
                         {
                             connection.setRequestProperty("cookie", cookie);
                         }
-                        
+
                         // This must be explicitly set for gzip progress 
tracking to work.
                         connection.setRequestProperty("Accept-Encoding", 
"gzip");
-    
+
                         // Handle the other headers
                         if (headers != null) {
                             addHeadersToRequest(connection, headers);
                         }
-        
+
                         connection.connect();
                         if (connection.getResponseCode() == 
HttpURLConnection.HTTP_NOT_MODIFIED) {
                             cached = true;
@@ -946,7 +946,6 @@ public class FileTransfer extends CordovaPlugin {
                             result = new 
PluginResult(PluginResult.Status.ERROR, "File plugin not found; cannot save 
downloaded file");
                         }
                     }
-                    
                 } catch (FileNotFoundException e) {
                     JSONObject error = 
createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection, e);
                     Log.e(LOG_TAG, error.toString(), e);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/007f9869/src/windows/FileTransferProxy.js
----------------------------------------------------------------------
diff --git a/src/windows/FileTransferProxy.js b/src/windows/FileTransferProxy.js
index d9e884e..635f3d6 100644
--- a/src/windows/FileTransferProxy.js
+++ b/src/windows/FileTransferProxy.js
@@ -81,6 +81,8 @@ exec(win, fail, 'FileTransfer', 'upload',
         var uploadId = options[9];
         var httpMethod = options[10];
 
+        var isMultipart = typeof headers["Content-Type"] === 'undefined';
+
         if (!filePath || (typeof filePath !== 'string')) {
             errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR,null,server));
             return;
@@ -131,120 +133,129 @@ exec(win, fail, 'FileTransfer', 'upload',
                 }
             }
 
-            // adding params supplied to request payload
-            var transferParts = [];
-            for (var key in params) {
-                if (params.hasOwnProperty(key)) {
-                    var contentPart = new 
Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart();
-                    contentPart.setHeader("Content-Disposition", "form-data; 
name=\"" + key + "\"");
-                    contentPart.setText(params[key]);
-                    transferParts.push(contentPart);
-                }
-            }
-
-            // Adding file to upload to request payload
-            var fileToUploadPart = new 
Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, 
fileName);
-            fileToUploadPart.setFile(storageFile);
-            transferParts.push(fileToUploadPart);
-
             // create download object. This will throw an exception if URL is 
malformed
             var uri = new Windows.Foundation.Uri(server);
+
+            var createUploadOperation;
             try {
-                uploader.createUploadAsync(uri, transferParts).then(
-                    function (upload) {
-                        // update internal TransferOperation object with newly 
created promise
-                        var uploadOperation = upload.startAsync();
-                        fileTransferOps[uploadId].promise = uploadOperation;
-
-                        uploadOperation.then(
-                            function (result) {
-                                // Update TransferOperation object with new 
state, delete promise property
-                                // since it is not actual anymore
-                                var currentUploadOp = 
fileTransferOps[uploadId];
-                                if (currentUploadOp) {
-                                    currentUploadOp.state = 
FileTransferOperation.DONE;
-                                    currentUploadOp.promise = null;
-                                }
+                if (isMultipart) {
+                    // adding params supplied to request payload
+                    var transferParts = [];
+                    for (var key in params) {
+                        if (params.hasOwnProperty(key)) {
+                            var contentPart = new 
Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart();
+                            contentPart.setHeader("Content-Disposition", 
"form-data; name=\"" + key + "\"");
+                            contentPart.setText(params[key]);
+                            transferParts.push(contentPart);
+                        }
+                    }
 
-                                var response = result.getResponseInformation();
-                                var ftResult = new 
FileUploadResult(result.progress.bytesSent, response.statusCode, '');
+                    // Adding file to upload to request payload
+                    var fileToUploadPart = new 
Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, 
fileName);
+                    fileToUploadPart.setFile(storageFile);
+                    transferParts.push(fileToUploadPart);
 
-                                // if server's response doesn't contain any 
data, then resolve operation now
-                                if (result.progress.bytesReceived === 0) {
-                                    successCallback(ftResult);
-                                    return;
-                                }
+                    createUploadOperation = uploader.createUploadAsync(uri, 
transferParts);
+                } else {
+                    createUploadOperation = 
WinJS.Promise.wrap(uploader.createUpload(uri, storageFile));
+                }
+            } catch (e) {
+                errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
+                return;
+            }
 
-                                // otherwise create a data reader, attached to 
response stream to get server's response
-                                var reader = new 
Windows.Storage.Streams.DataReader(result.getResultStreamAt(0));
-                                
reader.loadAsync(result.progress.bytesReceived).then(function (size) {
-                                    ftResult.response = 
reader.readString(size);
-                                    successCallback(ftResult);
-                                    reader.close();
-                                });
-                            },
-                            function (error) {
-                                var source = nativePathToCordova(filePath);
-
-                                // Handle download error here.
-                                // Wrap this routines into promise due to some 
async methods
-                                var getTransferError = new 
WinJS.Promise(function(resolve) {
-                                    if (error.message === 'Canceled') {
-                                        // If download was cancelled, message 
property will be specified
-                                        resolve(new FTErr(FTErr.ABORT_ERR, 
source, server, null, null, error));
+            createUploadOperation.then(
+                function (upload) {
+                    // update internal TransferOperation object with newly 
created promise
+                    var uploadOperation = upload.startAsync();
+                    fileTransferOps[uploadId].promise = uploadOperation;
+
+                    uploadOperation.then(
+                        function (result) {
+                            // Update TransferOperation object with new state, 
delete promise property
+                            // since it is not actual anymore
+                            var currentUploadOp = fileTransferOps[uploadId];
+                            if (currentUploadOp) {
+                                currentUploadOp.state = 
FileTransferOperation.DONE;
+                                currentUploadOp.promise = null;
+                            }
+
+                            var response = result.getResponseInformation();
+                            var ftResult = new 
FileUploadResult(result.progress.bytesSent, response.statusCode, '');
+
+                            // if server's response doesn't contain any data, 
then resolve operation now
+                            if (result.progress.bytesReceived === 0) {
+                                successCallback(ftResult);
+                                return;
+                            }
+
+                            // otherwise create a data reader, attached to 
response stream to get server's response
+                            var reader = new 
Windows.Storage.Streams.DataReader(result.getResultStreamAt(0));
+                            
reader.loadAsync(result.progress.bytesReceived).then(function (size) {
+                                ftResult.response = reader.readString(size);
+                                successCallback(ftResult);
+                                reader.close();
+                            });
+                        },
+                        function (error) {
+                            var source = nativePathToCordova(filePath);
+
+                            // Handle download error here.
+                            // Wrap this routines into promise due to some 
async methods
+                            var getTransferError = new 
WinJS.Promise(function(resolve) {
+                                if (error.message === 'Canceled') {
+                                    // If download was cancelled, message 
property will be specified
+                                    resolve(new FTErr(FTErr.ABORT_ERR, source, 
server, null, null, error));
+                                } else {
+                                    // in the other way, try to get response 
property
+                                    var response = 
upload.getResponseInformation();
+                                    if (!response) {
+                                        resolve(new 
FTErr(FTErr.CONNECTION_ERR, source, server));
                                     } else {
-                                        // in the other way, try to get 
response property
-                                        var response = 
upload.getResponseInformation();
-                                        if (!response) {
-                                            resolve(new 
FTErr(FTErr.CONNECTION_ERR, source, server));
-                                        } else {
-                                            var reader = new 
Windows.Storage.Streams.DataReader(upload.getResultStreamAt(0));
-                                            
reader.loadAsync(upload.progress.bytesReceived).then(function (size) {
-                                                var responseText = 
reader.readString(size);
-                                                resolve(new 
FTErr(FTErr.FILE_NOT_FOUND_ERR, source, server, response.statusCode, 
responseText, error));
-                                                reader.close();
-                                            });
-                                        }
+                                        var reader = new 
Windows.Storage.Streams.DataReader(upload.getResultStreamAt(0));
+                                        
reader.loadAsync(upload.progress.bytesReceived).then(function (size) {
+                                            var responseText = 
reader.readString(size);
+                                            resolve(new 
FTErr(FTErr.FILE_NOT_FOUND_ERR, source, server, response.statusCode, 
responseText, error));
+                                            reader.close();
+                                        });
                                     }
-                                });
-
-                                // Update TransferOperation object with new 
state, delete promise property
-                                // since it is not actual anymore
-                                var currentUploadOp = 
fileTransferOps[uploadId];
-                                if (currentUploadOp) {
-                                    currentUploadOp.state = 
FileTransferOperation.CANCELLED;
-                                    currentUploadOp.promise = null;
                                 }
+                            });
+
+                            // Update TransferOperation object with new state, 
delete promise property
+                            // since it is not actual anymore
+                            var currentUploadOp = fileTransferOps[uploadId];
+                            if (currentUploadOp) {
+                                currentUploadOp.state = 
FileTransferOperation.CANCELLED;
+                                currentUploadOp.promise = null;
+                            }
 
-                                // Cleanup, remove incompleted file
-                                getTransferError.then(function(transferError) {
-                                    storageFile.deleteAsync().then(function() {
-                                        errorCallback(transferError);
-                                    });
+                            // Cleanup, remove incompleted file
+                            getTransferError.then(function(transferError) {
+                                storageFile.deleteAsync().then(function() {
+                                    errorCallback(transferError);
                                 });
-                            },
-                            function (evt) {
-                                var progressEvent = new 
ProgressEvent('progress', {
-                                    loaded: evt.progress.bytesSent,
-                                    total: evt.progress.totalBytesToSend,
-                                    target: evt.resultFile
-                                });
-                                progressEvent.lengthComputable = true;
-                                successCallback(progressEvent, { keepCallback: 
true });
-                            }
-                        );
-                    },
-                    function (err) {
-                        var errorObj = new FTErr(FTErr.INVALID_URL_ERR);
-                        errorObj.exception = err;
-                        errorCallback(errorObj);
-                    }
-                );
-            } catch (e) {
-                errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
-            }
+                            });
+                        },
+                        function (evt) {
+                            var progressEvent = new ProgressEvent('progress', {
+                                loaded: evt.progress.bytesSent,
+                                total: evt.progress.totalBytesToSend,
+                                target: evt.resultFile
+                            });
+                            progressEvent.lengthComputable = true;
+                            successCallback(progressEvent, { keepCallback: 
true });
+                        }
+                    );
+                },
+                function (err) {
+                    var errorObj = new FTErr(FTErr.INVALID_URL_ERR);
+                    errorObj.exception = err;
+                    errorCallback(errorObj);
+                }
+            );
         }, function(err) {
-            errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, server, server, 
null, null, err));
+            errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, fileName, 
server, null, null, err));
         });
     },
 

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/007f9869/tests/tests.js
----------------------------------------------------------------------
diff --git a/tests/tests.js b/tests/tests.js
index fff1eef..48361c5 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -936,15 +936,41 @@ exports.defineAutoTests = function () {
                         done();
                     };
 
-                    var uploadOptionsPut        = new FileUploadOptions();
-                    uploadOptionsPut.fileKey    = "file";
-                    uploadOptionsPut.fileName   = fileName;
-                    uploadOptionsPut.mimeType   = "text/plain";
-                    uploadOptionsPut.params     = uploadParams;
-                    uploadOptionsPut.httpMethod = "PUT";
+                    uploadOptions.httpMethod = "PUT";
 
                     // NOTE: removing uploadOptions cause Android to timeout
-                    transfer.upload(localFilePath, fileURL, uploadWin, 
unexpectedCallbacks.httpFail, uploadOptionsPut);
+                    transfer.upload(localFilePath, fileURL, uploadWin, 
unexpectedCallbacks.httpFail, uploadOptions);
+                }, UPLOAD_TIMEOUT);
+
+                it("filetransfer.spec.32 should be able to upload a file 
(non-multipart)", function (done) {
+
+                    var fileURL = SERVER + '/upload';
+
+                    var uploadWin = function (uploadResult) {
+
+                        expect(uploadResult.bytesSent).toBeGreaterThan(0);
+                        expect(uploadResult.responseCode).toBe(200);
+                        expect(uploadResult.response).toBeDefined();
+                        if (uploadResult.response) {
+                            expect(uploadResult.response).toEqual(fileContents 
+ "\n");
+                        }
+                        expect(transfer.onprogress).toHaveBeenCalled();
+
+                        if (cordova.platformId === 'ios') {
+                            expect(uploadResult.headers).toBeDefined('Expected 
headers to be defined.');
+                            
expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type 
header to be defined.');
+                        }
+
+                        done();
+                    };
+
+                    // Content-Type header disables multipart
+                    uploadOptions.headers = {
+                        "Content-Type": "text/plain"
+                    };
+
+                    // NOTE: removing uploadOptions cause Android to timeout
+                    transfer.upload(localFilePath, fileURL, uploadWin, 
unexpectedCallbacks.httpFail, uploadOptions);
                 }, UPLOAD_TIMEOUT);
             });
         });


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to