mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-26 20:33:07 +08:00
[CB-1517] Properly report download progress for GZIP-encoded resources
This commit is contained in:
parent
36c33a5889
commit
282367c6d5
@ -41,6 +41,8 @@ import java.security.cert.CertificateException;
|
|||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
@ -99,11 +101,84 @@ public class FileTransfer extends CordovaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an interface method to an InputStream to return the number of bytes
|
||||||
|
* read from the raw stream. This is used to track total progress against
|
||||||
|
* the HTTP Content-Length header value from the server.
|
||||||
|
*/
|
||||||
|
private static abstract class TrackingInputStream extends FilterInputStream {
|
||||||
|
public TrackingInputStream(final InputStream in) {
|
||||||
|
super(in);
|
||||||
|
}
|
||||||
|
public abstract long getTotalRawBytesRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExposedGZIPInputStream extends GZIPInputStream {
|
||||||
|
public ExposedGZIPInputStream(final InputStream in) throws IOException {
|
||||||
|
super(in);
|
||||||
|
}
|
||||||
|
public Inflater getInflater() {
|
||||||
|
return inf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides raw bytes-read tracking for a GZIP input stream. Reports the
|
||||||
|
* total number of compressed bytes read from the input, rather than the
|
||||||
|
* number of uncompressed bytes.
|
||||||
|
*/
|
||||||
|
private static class TrackingGZIPInputStream extends TrackingInputStream {
|
||||||
|
private ExposedGZIPInputStream gzin;
|
||||||
|
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
|
||||||
|
super(gzin);
|
||||||
|
this.gzin = gzin;
|
||||||
|
}
|
||||||
|
public long getTotalRawBytesRead() {
|
||||||
|
return gzin.getInflater().getBytesRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides simple total-bytes-read tracking for an existing InputStream
|
||||||
|
*/
|
||||||
|
private static class TrackingHTTPInputStream extends TrackingInputStream {
|
||||||
|
private long bytesRead = 0;
|
||||||
|
public TrackingHTTPInputStream(InputStream stream) {
|
||||||
|
super(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int updateBytesRead(int newBytesRead) {
|
||||||
|
if (newBytesRead != -1) {
|
||||||
|
bytesRead += newBytesRead;
|
||||||
|
}
|
||||||
|
return newBytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return updateBytesRead(super.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buffer) throws IOException {
|
||||||
|
return updateBytesRead(super.read(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] bytes, int offset, int count) throws IOException {
|
||||||
|
return updateBytesRead(super.read(bytes, offset, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalRawBytesRead() {
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Works around a bug on Android 2.3.
|
* Works around a bug on Android 2.3.
|
||||||
* http://code.google.com/p/android/issues/detail?id=14562
|
* http://code.google.com/p/android/issues/detail?id=14562
|
||||||
*/
|
*/
|
||||||
private static final class DoneHandlerInputStream extends FilterInputStream {
|
private static final class DoneHandlerInputStream extends TrackingHTTPInputStream {
|
||||||
private boolean done;
|
private boolean done;
|
||||||
|
|
||||||
public DoneHandlerInputStream(InputStream stream) {
|
public DoneHandlerInputStream(InputStream stream) {
|
||||||
@ -409,7 +484,7 @@ public class FileTransfer extends CordovaPlugin {
|
|||||||
int responseCode = conn.getResponseCode();
|
int responseCode = conn.getResponseCode();
|
||||||
Log.d(LOG_TAG, "response code: " + responseCode);
|
Log.d(LOG_TAG, "response code: " + responseCode);
|
||||||
Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
|
Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
|
||||||
InputStream inStream = null;
|
TrackingInputStream inStream = null;
|
||||||
try {
|
try {
|
||||||
inStream = getInputStream(conn);
|
inStream = getInputStream(conn);
|
||||||
synchronized (context) {
|
synchronized (context) {
|
||||||
@ -485,11 +560,15 @@ public class FileTransfer extends CordovaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InputStream getInputStream(URLConnection conn) throws IOException {
|
private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||||
return new DoneHandlerInputStream(conn.getInputStream());
|
return new DoneHandlerInputStream(conn.getInputStream());
|
||||||
}
|
}
|
||||||
return conn.getInputStream();
|
String encoding = conn.getContentEncoding();
|
||||||
|
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
|
||||||
|
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
|
||||||
|
}
|
||||||
|
return new TrackingHTTPInputStream(conn.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
// always verify the host - don't check for certificate
|
// always verify the host - don't check for certificate
|
||||||
@ -701,6 +780,9 @@ public class FileTransfer extends CordovaPlugin {
|
|||||||
connection.setRequestProperty("cookie", cookie);
|
connection.setRequestProperty("cookie", cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This must be explicitly set for gzip progress tracking to work.
|
||||||
|
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
// Handle the other headers
|
// Handle the other headers
|
||||||
if (headers != null) {
|
if (headers != null) {
|
||||||
addHeadersToRequest(connection, headers);
|
addHeadersToRequest(connection, headers);
|
||||||
@ -711,14 +793,15 @@ public class FileTransfer extends CordovaPlugin {
|
|||||||
Log.d(LOG_TAG, "Download file:" + url);
|
Log.d(LOG_TAG, "Download file:" + url);
|
||||||
|
|
||||||
FileProgressResult progress = new FileProgressResult();
|
FileProgressResult progress = new FileProgressResult();
|
||||||
if (connection.getContentEncoding() == null) {
|
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
|
||||||
// Only trust content-length header if no gzip etc
|
// Only trust content-length header if we understand
|
||||||
|
// the encoding -- identity or gzip
|
||||||
progress.setLengthComputable(true);
|
progress.setLengthComputable(true);
|
||||||
progress.setTotal(connection.getContentLength());
|
progress.setTotal(connection.getContentLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
FileOutputStream outputStream = null;
|
FileOutputStream outputStream = null;
|
||||||
InputStream inputStream = null;
|
TrackingInputStream inputStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inputStream = getInputStream(connection);
|
inputStream = getInputStream(connection);
|
||||||
@ -733,12 +816,10 @@ public class FileTransfer extends CordovaPlugin {
|
|||||||
// write bytes to file
|
// write bytes to file
|
||||||
byte[] buffer = new byte[MAX_BUFFER_SIZE];
|
byte[] buffer = new byte[MAX_BUFFER_SIZE];
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
long totalBytes = 0;
|
|
||||||
while ((bytesRead = inputStream.read(buffer)) > 0) {
|
while ((bytesRead = inputStream.read(buffer)) > 0) {
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
totalBytes += bytesRead;
|
|
||||||
// Send a progress event.
|
// Send a progress event.
|
||||||
progress.setLoaded(totalBytes);
|
progress.setLoaded(inputStream.getTotalRawBytesRead());
|
||||||
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
|
||||||
progressResult.setKeepCallback(true);
|
progressResult.setKeepCallback(true);
|
||||||
context.sendPluginResult(progressResult);
|
context.sendPluginResult(progressResult);
|
||||||
|
Loading…
Reference in New Issue
Block a user