mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-20 23:56:20 +08:00
Add progress callbacks, abort for FileTransfer.upload and FileTransfer.download
This commit is contained in:
parent
3688fca126
commit
610e0c984a
63
framework/src/org/apache/cordova/FileProgressResult.java
Normal file
63
framework/src/org/apache/cordova/FileProgressResult.java
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Encapsulates in-progress status of uploading or downloading a file to a remote server.
|
||||
*/
|
||||
public class FileProgressResult {
|
||||
|
||||
private boolean lengthComputable = false; // declares whether total is known
|
||||
private long loaded = 0; // bytes sent so far
|
||||
private long total = 0; // bytes total, if known
|
||||
|
||||
public boolean getLengthComputable() {
|
||||
return lengthComputable;
|
||||
}
|
||||
|
||||
public void setLengthComputable(boolean computable) {
|
||||
this.lengthComputable = computable;
|
||||
}
|
||||
|
||||
public long getLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public void setLoaded(long bytes) {
|
||||
this.loaded = bytes;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long bytes) {
|
||||
this.total = bytes;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{loaded:" + loaded +
|
||||
",total:" + total +
|
||||
",lengthComputable:" + (lengthComputable ? "true" : "false") + "}");
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
@ -63,29 +64,44 @@ public class FileTransfer extends Plugin {
|
||||
public static int FILE_NOT_FOUND_ERR = 1;
|
||||
public static int INVALID_URL_ERR = 2;
|
||||
public static int CONNECTION_ERR = 3;
|
||||
public static int ABORTED_ERR = 4;
|
||||
|
||||
private static HashSet abortTriggered = new HashSet();
|
||||
|
||||
private SSLSocketFactory defaultSSLSocketFactory = null;
|
||||
private HostnameVerifier defaultHostnameVerifier = null;
|
||||
|
||||
static class AbortException extends Exception {
|
||||
public AbortException(String str) {
|
||||
super(str);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.cordova.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
String source = null;
|
||||
String target = null;
|
||||
try {
|
||||
source = args.getString(0);
|
||||
target = args.getString(1);
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Missing source or target");
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
|
||||
}
|
||||
if (action.equals("upload") || action.equals("download")) {
|
||||
String source = null;
|
||||
String target = null;
|
||||
try {
|
||||
source = args.getString(0);
|
||||
target = args.getString(1);
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Missing source or target");
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
|
||||
}
|
||||
|
||||
if (action.equals("upload")) {
|
||||
return upload(URLDecoder.decode(source), target, args);
|
||||
} else if (action.equals("download")) {
|
||||
return download(source, target, args.optBoolean(2));
|
||||
if (action.equals("upload")) {
|
||||
return upload(URLDecoder.decode(source), target, args, callbackId);
|
||||
} else if (action.equals("download")) {
|
||||
String objectId = args.getString(2);
|
||||
boolean trustEveryone = args.optBoolean(3);
|
||||
return download(source, target, trustEveryone, objectId, callbackId);
|
||||
}
|
||||
} else if (action.equals("abort")) {
|
||||
return abort(args);
|
||||
} else {
|
||||
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
}
|
||||
@ -96,6 +112,7 @@ public class FileTransfer extends Plugin {
|
||||
* @param source Full path of the file on the file system
|
||||
* @param target URL of the server to receive the file
|
||||
* @param args JSON Array of args
|
||||
* @param callbackId callback id for optional progress reports
|
||||
*
|
||||
* args[2] fileKey Name of file request parameter
|
||||
* args[3] fileName File name to be used on server
|
||||
@ -103,7 +120,7 @@ public class FileTransfer extends Plugin {
|
||||
* args[5] params key:value pairs of user-defined parameters
|
||||
* @return FileUploadResult containing result of upload request
|
||||
*/
|
||||
private PluginResult upload(String source, String target, JSONArray args) {
|
||||
private PluginResult upload(String source, String target, JSONArray args, String callbackId) {
|
||||
Log.d(LOG_TAG, "upload " + source + " to " + target);
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
@ -121,6 +138,7 @@ public class FileTransfer extends Plugin {
|
||||
if (headers == null && params != null) {
|
||||
headers = params.optJSONObject("headers");
|
||||
}
|
||||
String objectId = args.getString(9);
|
||||
|
||||
Log.d(LOG_TAG, "fileKey: " + fileKey);
|
||||
Log.d(LOG_TAG, "fileName: " + fileName);
|
||||
@ -129,9 +147,11 @@ public class FileTransfer extends Plugin {
|
||||
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
|
||||
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
|
||||
Log.d(LOG_TAG, "headers: " + headers);
|
||||
Log.d(LOG_TAG, "objectId: " + objectId);
|
||||
|
||||
// Create return object
|
||||
FileUploadResult result = new FileUploadResult();
|
||||
FileProgressResult progress = new FileProgressResult();
|
||||
|
||||
// Get a input stream of the file on the phone
|
||||
FileInputStream fileInputStream = (FileInputStream) getPathFromUri(source);
|
||||
@ -285,6 +305,21 @@ public class FileTransfer extends Plugin {
|
||||
bytesAvailable = fileInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
||||
if (objectId != null) {
|
||||
// Only send progress callbacks if the JS code sent us an object ID,
|
||||
// so we don't spam old versions with unrecognized callbacks.
|
||||
Log.d(LOG_TAG, "****** About to send a progress result from upload");
|
||||
progress.setLoaded(totalBytes);
|
||||
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||
progressResult.setKeepCallback(true);
|
||||
success(progressResult, callbackId);
|
||||
}
|
||||
synchronized (abortTriggered) {
|
||||
if (objectId != null && abortTriggered.contains(objectId)) {
|
||||
abortTriggered.remove(objectId);
|
||||
throw new AbortException("upload aborted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send multipart form data necessary after file data...
|
||||
@ -342,6 +377,10 @@ public class FileTransfer extends Plugin {
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
} catch (AbortException e) {
|
||||
JSONObject error = createFileTransferError(ABORTED_ERR, source, target, conn);
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (Throwable t) {
|
||||
// Shouldn't happen, but will
|
||||
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
|
||||
@ -459,7 +498,7 @@ public class FileTransfer extends Plugin {
|
||||
* @param target Full path of the file on the file system
|
||||
* @return JSONObject the downloaded file
|
||||
*/
|
||||
private PluginResult download(String source, String target, boolean trustEveryone) {
|
||||
private PluginResult download(String source, String target, boolean trustEveryone, String objectId, String callbackId) {
|
||||
Log.d(LOG_TAG, "download " + source + " to " + target);
|
||||
|
||||
HttpURLConnection connection = null;
|
||||
@ -523,12 +562,36 @@ public class FileTransfer extends Plugin {
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead = 0;
|
||||
long totalBytes = 0;
|
||||
FileProgressResult progress = new FileProgressResult();
|
||||
|
||||
if (connection.getContentEncoding() == null) {
|
||||
// Only trust content-length header if no gzip etc
|
||||
progress.setLengthComputable(true);
|
||||
progress.setTotal(connection.getContentLength());
|
||||
}
|
||||
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
|
||||
// write bytes to file
|
||||
while ((bytesRead = inputStream.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
totalBytes += bytesRead;
|
||||
if (objectId != null) {
|
||||
// Only send progress callbacks if the JS code sent us an object ID,
|
||||
// so we don't spam old versions with unrecognized callbacks.
|
||||
Log.d(LOG_TAG, "****** About to send a progress result from download");
|
||||
progress.setLoaded(totalBytes);
|
||||
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||
progressResult.setKeepCallback(true);
|
||||
success(progressResult, callbackId);
|
||||
}
|
||||
synchronized (abortTriggered) {
|
||||
if (objectId != null && abortTriggered.contains(objectId)) {
|
||||
abortTriggered.remove(objectId);
|
||||
throw new AbortException("download aborted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputStream.close();
|
||||
@ -621,4 +684,23 @@ public class FileTransfer extends Plugin {
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort an ongoing upload or download.
|
||||
*
|
||||
* @param args args
|
||||
*/
|
||||
private PluginResult abort(JSONArray args) {
|
||||
String objectId;
|
||||
try {
|
||||
objectId = args.getString(0);
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Missing objectId");
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing objectId");
|
||||
}
|
||||
synchronized (abortTriggered) {
|
||||
abortTriggered.add(objectId);
|
||||
}
|
||||
return new PluginResult(PluginResult.Status.OK);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public class FileUploadResult {
|
||||
private long bytesSent = 0; // bytes sent
|
||||
private int responseCode = -1; // HTTP response code
|
||||
private String response = null; // HTTP response
|
||||
private String objectId = null; // FileTransfer object id
|
||||
|
||||
public long getBytesSent() {
|
||||
return bytesSent;
|
||||
@ -54,10 +55,19 @@ public class FileUploadResult {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{bytesSent:" + bytesSent +
|
||||
",responseCode:" + responseCode +
|
||||
",response:" + JSONObject.quote(response) + "}");
|
||||
",response:" + JSONObject.quote(response) +
|
||||
",objectId:" + JSONObject.quote(objectId) + "}");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user