diff --git a/README.md b/README.md index a70bc800..b90992df 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,10 @@ Here's a quick example: const options = { method: 'post', data: { id: 12, message: 'test' }, - headers: { Authorization: 'OAuth2: token' } + headers: { Authorization: 'OAuth2: token' }, + onProgress: function(progressData) { + console.log((progressData.transferred / progressData.total * 100) + ' percent complete') + } }; cordova.plugin.http.sendRequest('https://google.com/', options, function(response) { diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java b/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java index 69b1fa55..f9fa2c7a 100644 --- a/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java +++ b/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java @@ -1,5 +1,7 @@ package com.silkimen.cordovahttp; +import android.util.Log; + import java.io.File; import java.net.URI; @@ -9,17 +11,27 @@ import com.silkimen.http.HttpRequest; import com.silkimen.http.TLSConfiguration; +import org.apache.cordova.PluginResult; import org.apache.cordova.file.FileUtils; +import org.json.JSONException; import org.json.JSONObject; class CordovaHttpDownload extends CordovaHttpBase { private String filePath; + private boolean hasProgressCallback; + public CordovaHttpDownload(String url, JSONObject headers, String filePath, int connectTimeout, int readTimeout, - boolean followRedirects, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) { + boolean followRedirects, boolean hasProgressCallback, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) { super("GET", url, headers, connectTimeout, readTimeout, followRedirects, "text", tlsConfiguration, callbackContext); this.filePath = filePath; + this.hasProgressCallback = hasProgressCallback; + } + + @Override + protected HttpRequest createRequest() throws JSONException { + return new HttpRequest(this.url, this.method, this.hasProgressCallback); } @Override @@ -37,5 +49,30 @@ protected void processResponse(HttpRequest request, CordovaHttpResponse response } else { response.setErrorMessage("There was an error downloading the file"); } + + request.progress(null); + } + + @Override + protected void sendBody(HttpRequest request) throws Exception { + if (hasProgressCallback) { + int fileLength = request.getConnection().getContentLength(); + request.progress(new HttpRequest.UploadProgress() { + public void onUpload(long transferred, long total) { + JSONObject json = new JSONObject(); + try { + json.put("isProgress", true); + json.put("transferred", transferred); + json.put("total", fileLength); + + PluginResult result = new PluginResult(PluginResult.Status.OK, json); + result.setKeepCallback(true); + callbackContext.getCallbackContext().sendPluginResult(result); + } catch (JSONException e) { + Log.e(TAG, "onUpload progress error", e); + } + } + }); + } } } diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java index 9a7d34ad..c1d2b575 100644 --- a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java +++ b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java @@ -164,11 +164,12 @@ private boolean uploadFiles(final JSONArray args, final CallbackContext callback boolean followRedirect = args.getBoolean(6); String responseType = args.getString(7); Integer reqId = args.getInt(8); + boolean hasProgressCallback = args.getBoolean(9); 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); + hasProgressCallback, responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), observableCallbackContext); startRequest(reqId, observableCallbackContext, upload); @@ -183,11 +184,12 @@ private boolean downloadFile(final JSONArray args, final CallbackContext callbac int readTimeout = args.getInt(4) * 1000; boolean followRedirect = args.getBoolean(5); Integer reqId = args.getInt(6); + boolean hasProgressCallback = args.getBoolean(7); CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId); CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, connectTimeout, readTimeout, - followRedirect, this.tlsConfiguration, observableCallbackContext); + followRedirect, hasProgressCallback, this.tlsConfiguration, observableCallbackContext); startRequest(reqId, observableCallbackContext, download); diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java index d37464d2..f4fd7df8 100644 --- a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java +++ b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java @@ -5,6 +5,7 @@ import android.database.Cursor; import android.net.Uri; import android.provider.OpenableColumns; +import android.util.Log; import android.webkit.MimeTypeMap; import com.silkimen.http.HttpRequest; @@ -17,22 +18,31 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSocketFactory; +import org.apache.cordova.PluginResult; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; class CordovaHttpUpload extends CordovaHttpBase { private JSONArray filePaths; private JSONArray uploadNames; private Context applicationContext; + private boolean hasProgressCallback; public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int connectTimeout, int readTimeout, - boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration, + boolean followRedirects, boolean hasProgressCallback, String responseType, TLSConfiguration tlsConfiguration, Context applicationContext, CordovaObservableCallbackContext callbackContext) { super("POST", url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext); this.filePaths = filePaths; this.uploadNames = uploadNames; this.applicationContext = applicationContext; + this.hasProgressCallback = hasProgressCallback; + } + + @Override + protected HttpRequest createRequest() throws JSONException { + return new HttpRequest(this.url, this.method, this.hasProgressCallback); } @Override @@ -60,6 +70,25 @@ protected void sendBody(HttpRequest request) throws Exception { request.part(uploadName, fileName, mimeType, inputStream); } + + if (hasProgressCallback) { + request.progress(new HttpRequest.UploadProgress() { + public void onUpload(long transferred, long total) { + JSONObject json = new JSONObject(); + try { + json.put("isProgress", true); + json.put("transferred", transferred); + json.put("total", total); + + PluginResult result = new PluginResult(PluginResult.Status.OK, json); + result.setKeepCallback(true); + callbackContext.getCallbackContext().sendPluginResult(result); + } catch (JSONException e) { + Log.e(TAG, "onUpload progress error", e); + } + } + }); + } } } diff --git a/src/android/com/silkimen/http/HttpRequest.java b/src/android/com/silkimen/http/HttpRequest.java index 7e638bb1..bfc04ee8 100644 --- a/src/android/com/silkimen/http/HttpRequest.java +++ b/src/android/com/silkimen/http/HttpRequest.java @@ -1328,6 +1328,8 @@ public String run() { private final String requestMethod; + private boolean hasProgressCallback = false; + private RequestOutputStream output; private boolean multipart; @@ -1378,6 +1380,16 @@ public HttpRequest(final URL url, final String method) throws HttpRequestExcepti this.requestMethod = method; } + public HttpRequest(final CharSequence url, final String method, boolean hasProgressCallback) throws HttpRequestException { + try { + this.url = new URL(url.toString()); + } catch (MalformedURLException e) { + throw new HttpRequestException(e); + } + this.requestMethod = method; + this.hasProgressCallback = hasProgressCallback; + } + private Proxy createProxy() { return new Proxy(HTTP, new InetSocketAddress(httpProxyHost, httpProxyPort)); } @@ -2553,7 +2565,9 @@ private HttpRequest incrementTotalSize(final long size) { * @throws IOException */ protected HttpRequest closeOutput() throws IOException { - progress(null); + if(!this.hasProgressCallback){ + progress(null); + } if (output == null) return this; if (multipart) diff --git a/src/ios/CordovaHttpPlugin.m b/src/ios/CordovaHttpPlugin.m index 97c98e01..3ca7feb0 100644 --- a/src/ios/CordovaHttpPlugin.m +++ b/src/ios/CordovaHttpPlugin.m @@ -494,13 +494,22 @@ - (void)uploadFiles:(CDVInvokedUrlCommand*)command { [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; return; } - } progress:nil success:^(NSURLSessionTask *task, id responseObject) { + } progress:^(NSProgress *progress) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setValue:[NSNumber numberWithBool:YES] forKey:@"isProgress"]; + [dictionary setValue:[NSNumber numberWithLongLong:progress.completedUnitCount] forKey:@"transferred"]; + [dictionary setValue:[NSNumber numberWithLongLong:progress.totalUnitCount] forKey:@"total"]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } 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]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:NO]]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; } failure:^(NSURLSessionTask *task, NSError *error) { @@ -510,6 +519,7 @@ - (void)uploadFiles:(CDVInvokedUrlCommand*)command { [self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:NO]]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; }]; @@ -546,7 +556,15 @@ - (void)downloadFile:(CDVInvokedUrlCommand*)command { [[SDNetworkActivityIndicator sharedActivityIndicator] startActivity]; @try { - NSURLSessionDataTask *task = [manager GET:url parameters:nil progress: nil success:^(NSURLSessionTask *task, id responseObject) { + NSURLSessionDataTask *task = [manager GET:url parameters:nil progress:^(NSProgress *progress) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setValue:[NSNumber numberWithBool:YES] forKey:@"isProgress"]; + [dictionary setValue:[NSNumber numberWithLongLong:progress.completedUnitCount] forKey:@"transferred"]; + [dictionary setValue:[NSNumber numberWithLongLong:progress.totalUnitCount] forKey:@"total"]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } success:^(NSURLSessionTask *task, id responseObject) { [weakSelf removeRequest:reqId]; /* * @@ -605,6 +623,7 @@ - (void)downloadFile:(CDVInvokedUrlCommand*)command { [dictionary setObject:[filePlugin getDirectoryEntry:filePath isDirectory:NO] forKey:@"file"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:NO]]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; } failure:^(NSURLSessionTask *task, NSError *error) { @@ -614,6 +633,7 @@ - (void)downloadFile:(CDVInvokedUrlCommand*)command { [dictionary setObject:@"There was an error downloading the file" forKey:@"error"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:NO]]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; [[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity]; }]; diff --git a/www/helpers.js b/www/helpers.js index ed070fe6..a2dd3b6c 100644 --- a/www/helpers.js +++ b/www/helpers.js @@ -510,7 +510,8 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64, serializer: checkSerializer(options.serializer || globals.serializer), connectTimeout: checkTimeoutValue(options.connectTimeout || globals.connectTimeout), readTimeout: checkTimeoutValue(options.readTimeout || globals.readTimeout), - timeout: checkTimeoutValue(options.timeout || globals.timeout) + timeout: checkTimeoutValue(options.timeout || globals.timeout), + onProgress: options.onProgress }; } }; diff --git a/www/public-interface.js b/www/public-interface.js index 66b52612..2480fe7b 100644 --- a/www/public-interface.js +++ b/www/public-interface.js @@ -169,6 +169,7 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf var onSuccess = helpers.injectCookieHandler(url, helpers.injectRawResponseHandler(options.responseType, success, failure)); var reqId = helpers.nextRequestId(); + var hasProgressCallback = options.onProgress != null; switch (options.method) { case 'post': @@ -180,12 +181,24 @@ 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]); + exec(function(resp) { + if (resp != null && resp.isProgress) { + options.onProgress(resp); + } else { + onSuccess(resp); + } + }, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, reqId, hasProgressCallback]); break; case 'download': var filePath = helpers.checkDownloadFilePath(options.filePath); var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success)); - exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, filePath, options.connectTimeout, options.readTimeout, options.followRedirect, reqId]); + exec(function(resp) { + if (resp != null && resp.isProgress) { + options.onProgress(resp); + } else { + onDownloadSuccess(resp); + } + }, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, filePath, options.connectTimeout, options.readTimeout, options.followRedirect, reqId, hasProgressCallback]); break; default: exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, reqId]);