Refactored downloadFile, added stopDownload

This commit is contained in:
Chris Dell 2015-11-23 16:29:25 +00:00
parent de4f913bff
commit b603084ce7
10 changed files with 302 additions and 120 deletions

View File

@ -1,10 +1,24 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
typedef void (^DownloaderCallback)(NSNumber*, NSNumber*);
typedef void (^ErrorCallback)(NSError*); typedef void (^ErrorCallback)(NSError*);
typedef void (^DownloaderCallback)(NSNumber*, NSNumber*, NSNumber*); typedef void (^BeginCallback)(NSNumber*, NSNumber*, NSDictionary*);
typedef void (^ProgressCallback)(NSNumber*, NSNumber*);
@interface DownloadParams : NSObject
@property (copy) NSString* fromUrl;
@property (copy) NSString* toFile;
@property (copy) DownloaderCallback callback; // Download has finished (data written)
@property (copy) ErrorCallback errorCallback; // Something went wrong
@property (copy) BeginCallback beginCallback; // Download has started (headers received)
@property (copy) ProgressCallback progressCallback; // Download is progressing
@end
@interface Downloader : NSObject <NSURLConnectionDelegate> @interface Downloader : NSObject <NSURLConnectionDelegate>
- (void)downloadFile:(NSString*)urlStr toFile:(NSString*)downloadPath callback:(DownloaderCallback)callback errorCallback:(ErrorCallback)errorCallback progressCallback:(DownloaderCallback)progressCallback; - (void)downloadFile:(DownloadParams*)params;
- (void)stopDownload;
@end @end

View File

@ -1,11 +1,14 @@
#import "Downloader.h" #import "Downloader.h"
@implementation DownloadParams
@end
@interface Downloader() @interface Downloader()
@property (copy) DownloaderCallback callback; @property (copy) DownloadParams* params;
@property (copy) ErrorCallback errorCallback;
@property (copy) DownloaderCallback progressCallback;
@property (retain) NSURLConnection* connection;
@property (retain) NSNumber* statusCode; @property (retain) NSNumber* statusCode;
@property (retain) NSNumber* contentLength; @property (retain) NSNumber* contentLength;
@property (retain) NSNumber* bytesWritten; @property (retain) NSNumber* bytesWritten;
@ -16,42 +19,40 @@
@implementation Downloader @implementation Downloader
- (void)downloadFile:(NSString*)urlStr toFile:(NSString*)downloadPath callback:(DownloaderCallback)callback errorCallback:(ErrorCallback)errorCallback progressCallback:(DownloaderCallback)progressCallback - (void)downloadFile:(DownloadParams*)params
{ {
_callback = callback; _params = params;
_errorCallback = errorCallback;
_progressCallback = progressCallback;
_bytesWritten = 0; _bytesWritten = 0;
NSURL* url = [NSURL URLWithString:urlStr]; NSURL* url = [NSURL URLWithString:_params.fromUrl];
NSMutableURLRequest* downloadRequest = [NSMutableURLRequest requestWithURL:url NSMutableURLRequest* downloadRequest = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30]; timeoutInterval:30];
[[NSFileManager defaultManager] createFileAtPath:downloadPath contents:nil attributes:nil]; [[NSFileManager defaultManager] createFileAtPath:_params.toFile contents:nil attributes:nil];
_fileHandle = [NSFileHandle fileHandleForWritingAtPath:downloadPath]; _fileHandle = [NSFileHandle fileHandleForWritingAtPath:_params.toFile];
if (!_fileHandle) { if (!_fileHandle) {
NSError* error = [NSError errorWithDomain:@"Downloader" code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to create target file at path: %@", downloadPath]}]; NSError* error = [NSError errorWithDomain:@"Downloader" code:NSURLErrorFileDoesNotExist userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"Failed to create target file at path: %@", _params.toFile]}];
return _errorCallback(error); return _params.errorCallback(error);
} }
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self startImmediately:NO]; _connection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; [_connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[connection start]; [_connection start];
} }
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{ {
[_fileHandle closeFile]; [_fileHandle closeFile];
return _errorCallback(error); return _params.errorCallback(error);
} }
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
@ -60,6 +61,8 @@
_statusCode = [NSNumber numberWithLong:httpUrlResponse.statusCode]; _statusCode = [NSNumber numberWithLong:httpUrlResponse.statusCode];
_contentLength = [NSNumber numberWithLong: httpUrlResponse.expectedContentLength]; _contentLength = [NSNumber numberWithLong: httpUrlResponse.expectedContentLength];
return _params.beginCallback(_statusCode, _contentLength, httpUrlResponse.allHeaderFields);
} }
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
@ -69,7 +72,7 @@
_bytesWritten = [NSNumber numberWithUnsignedInteger:[_bytesWritten unsignedIntegerValue] + data.length]; _bytesWritten = [NSNumber numberWithUnsignedInteger:[_bytesWritten unsignedIntegerValue] + data.length];
return _progressCallback(_statusCode, _contentLength, _bytesWritten); return _params.progressCallback(_contentLength, _bytesWritten);
} }
} }
@ -77,7 +80,12 @@
{ {
[_fileHandle closeFile]; [_fileHandle closeFile];
return _callback(_statusCode, _contentLength, _bytesWritten); return _params.callback(_statusCode, _bytesWritten);
}
- (void)stopDownload
{
[_connection cancel];
} }
@end @end

View File

@ -138,19 +138,30 @@ var RNFS = {
.catch(convertError); .catch(convertError);
}, },
downloadFile(url, filepath, progress) { downloadFile(fromUrl, toFile, begin, progress) {
var jobId = getJobId(); var jobId = getJobId();
var subscriptionIos, subscriptionAndroid; var subscriptionIos, subscriptionAndroid;
if (progress) { if (!begin) begin = (info) => {
console.log('Download begun:', info);
};
if (begin) {
// Two different styles of subscribing to events for different platforms, hmmm.... // Two different styles of subscribing to events for different platforms, hmmm....
if (NativeAppEventEmitter.addListener) if (NativeAppEventEmitter.addListener)
subscriptionIos = NativeAppEventEmitter.addListener('DownloadProgress-' + jobId, progress); subscriptionIos = NativeAppEventEmitter.addListener('DownloadBegin-' + jobId, begin);
if (DeviceEventEmitter.addListener)
subscriptionAndroid = DeviceEventEmitter.addListener('DownloadBegin-' + jobId, begin);
}
if (progress) {
if (NativeAppEventEmitter.addListener) if (NativeAppEventEmitter.addListener)
subscriptionIos = NativeAppEventEmitter.addListener('DownloadProgress-' + jobId, progress);
if (DeviceEventEmitter.addListener)
subscriptionAndroid = DeviceEventEmitter.addListener('DownloadProgress-' + jobId, progress); subscriptionAndroid = DeviceEventEmitter.addListener('DownloadProgress-' + jobId, progress);
} }
return _downloadFile(url, filepath, jobId) return _downloadFile(fromUrl, toFile, jobId)
.then(res => { .then(res => {
if (subscriptionIos) subscriptionIos.remove(); if (subscriptionIos) subscriptionIos.remove();
if (subscriptionAndroid) subscriptionAndroid.remove(); if (subscriptionAndroid) subscriptionAndroid.remove();

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
8BF740771C033A2E0057A1E7 /* Downloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BF740761C033A2E0057A1E7 /* Downloader.m */; };
F1E59BDF1ADD662800ACA28A /* RNFSManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F1E59BDE1ADD662800ACA28A /* RNFSManager.m */; }; F1E59BDF1ADD662800ACA28A /* RNFSManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F1E59BDE1ADD662800ACA28A /* RNFSManager.m */; };
F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */ = {isa = PBXBuildFile; fileRef = F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */; }; F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */ = {isa = PBXBuildFile; fileRef = F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -24,6 +25,8 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
8BF740751C033A2E0057A1E7 /* Downloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downloader.h; sourceTree = "<group>"; };
8BF740761C033A2E0057A1E7 /* Downloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downloader.m; sourceTree = "<group>"; };
F12AFB9B1ADAF8F800E0535D /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFS.a; sourceTree = BUILT_PRODUCTS_DIR; }; F12AFB9B1ADAF8F800E0535D /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFS.a; sourceTree = BUILT_PRODUCTS_DIR; };
F1E59BDD1ADD662800ACA28A /* RNFSManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFSManager.h; sourceTree = "<group>"; }; F1E59BDD1ADD662800ACA28A /* RNFSManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFSManager.h; sourceTree = "<group>"; };
F1E59BDE1ADD662800ACA28A /* RNFSManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFSManager.m; sourceTree = "<group>"; }; F1E59BDE1ADD662800ACA28A /* RNFSManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFSManager.m; sourceTree = "<group>"; };
@ -49,6 +52,8 @@
F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */, F1EB08BA1AFD0E6A008F8F2B /* NSArray+Map.m */,
F1E59BDD1ADD662800ACA28A /* RNFSManager.h */, F1E59BDD1ADD662800ACA28A /* RNFSManager.h */,
F1E59BDE1ADD662800ACA28A /* RNFSManager.m */, F1E59BDE1ADD662800ACA28A /* RNFSManager.m */,
8BF740751C033A2E0057A1E7 /* Downloader.h */,
8BF740761C033A2E0057A1E7 /* Downloader.m */,
F12AFB9C1ADAF8F800E0535D /* Products */, F12AFB9C1ADAF8F800E0535D /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
@ -119,6 +124,7 @@
files = ( files = (
F1E59BDF1ADD662800ACA28A /* RNFSManager.m in Sources */, F1E59BDF1ADD662800ACA28A /* RNFSManager.m in Sources */,
F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */, F1EB08BB1AFD0E6A008F8F2B /* NSArray+Map.m in Sources */,
8BF740771C033A2E0057A1E7 /* Downloader.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -12,6 +12,12 @@
#import "Downloader.h" #import "Downloader.h"
#import "RCTEventDispatcher.h" #import "RCTEventDispatcher.h"
@interface RNFSManager()
@property (retain) NSMutableDictionary* downloaders;
@end
@implementation RNFSManager @implementation RNFSManager
@synthesize bridge = _bridge; @synthesize bridge = _bridge;
@ -148,24 +154,51 @@ RCT_EXPORT_METHOD(downloadFile:(NSString *)urlStr
callback:(RCTResponseSenderBlock)callback) callback:(RCTResponseSenderBlock)callback)
{ {
DownloaderCallback downloaderSuccessCallback = ^(NSNumber* statusCode, NSNumber* contentLength, NSNumber* bytesWritten) { DownloadParams* params = [DownloadParams alloc];
return callback(@[[NSNull null], [NSNumber numberWithBool:YES], filepath]);
params.fromUrl = urlStr;
params.toFile = filepath;
params.callback = ^(NSNumber* statusCode, NSNumber* bytesWritten) {
return callback(@[[NSNull null], @{@"jobId": jobId,
@"statusCode": statusCode,
@"bytesWritten": bytesWritten}]);
}; };
ErrorCallback downloaderErrorCallback = ^(NSError* error) { params.errorCallback = ^(NSError* error) {
return callback([self makeErrorPayload:error]); return callback([self makeErrorPayload:error]);
}; };
DownloaderCallback downloaderProgressCallback = ^(NSNumber* statusCode, NSNumber* contentLength, NSNumber* bytesWritten) { params.beginCallback = ^(NSNumber* statusCode, NSNumber* contentLength, NSDictionary* headers) {
[self.bridge.eventDispatcher sendAppEventWithName:[NSString stringWithFormat:@"DownloadProgress-%@", jobId] [self.bridge.eventDispatcher sendAppEventWithName:[NSString stringWithFormat:@"DownloadBegin-%@", jobId]
body:@{@"statusCode": statusCode, body:@{@"jobId": jobId,
@"statusCode": statusCode,
@"contentLength": contentLength, @"contentLength": contentLength,
@"headers": headers}];
};
params.progressCallback = ^(NSNumber* contentLength, NSNumber* bytesWritten) {
[self.bridge.eventDispatcher sendAppEventWithName:[NSString stringWithFormat:@"DownloadProgress-%@", jobId]
body:@{@"contentLength": contentLength,
@"bytesWritten": bytesWritten}]; @"bytesWritten": bytesWritten}];
}; };
if (self.downloaders) self.downloaders = [NSMutableDictionary alloc];
Downloader* downloader = [Downloader alloc]; Downloader* downloader = [Downloader alloc];
[downloader downloadFile:urlStr toFile:filepath callback:downloaderSuccessCallback errorCallback:downloaderErrorCallback progressCallback:downloaderProgressCallback]; [downloader downloadFile:params];
[self.downloaders setValue:downloader forKey:[jobId stringValue]];
}
RCT_EXPORT_METHOD(stopDownload:(NSNumber *)jobId)
{
Downloader* downloader = [self.downloaders objectForKey:[jobId stringValue]];
if (downloader != nil) {
[downloader stopDownload];
}
} }
RCT_EXPORT_METHOD(pathForBundle:(NSString *)bundleNamed RCT_EXPORT_METHOD(pathForBundle:(NSString *)bundleNamed

View File

@ -0,0 +1,25 @@
package com.rnfs;
import java.io.File;
import java.net.URL;
import java.util.*;
public class DownloadParams {
public interface OnTaskCompleted {
void onTaskCompleted(DownloadResult res);
}
public interface OnDownloadBegin {
void onDownloadBegin(int statusCode, int contentLength, Map<String, String> headers);
}
public interface OnDownloadProgress {
void onDownloadProgress(int contentLength, int bytesWritten);
}
public URL src;
public File dest;
public OnTaskCompleted onTaskCompleted;
public OnDownloadBegin onDownloadBegin;
public OnDownloadProgress onDownloadProgress;
}

View File

@ -0,0 +1,7 @@
package com.rnfs;
public class DownloadResult {
public int statusCode;
public int bytesWritten;
public Exception exception;
}

View File

@ -0,0 +1,107 @@
package com.rnfs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import android.os.AsyncTask;
public class Downloader extends AsyncTask<DownloadParams, int[], DownloadResult> {
private DownloadParams mParam;
private AtomicBoolean mAbort = new AtomicBoolean(false);
protected DownloadResult doInBackground(DownloadParams... params) {
mParam = params[0];
DownloadResult res = new DownloadResult();
try {
this.download(mParam, res);
mParam.onTaskCompleted.onTaskCompleted(res);
} catch (Exception ex) {
res.exception = ex;
mParam.onTaskCompleted.onTaskCompleted(res);
return res;
}
return res;
}
private void download(DownloadParams param, DownloadResult res) throws IOException {
InputStream input = null;
OutputStream output = null;
try {
HttpURLConnection connection = (HttpURLConnection)param.src.openConnection();
connection.setConnectTimeout(5000);
connection.connect();
int statusCode = connection.getResponseCode();
int lengthOfFile = connection.getContentLength();
Map<String, List<String>> headers = connection.getHeaderFields();
Map<String, String> headersFlat = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String headerKey = entry.getKey();
String valueKey = entry.getValue().get(0);
if (headerKey != null && valueKey != null) {
headersFlat.put(headerKey, valueKey);
}
}
mParam.onDownloadBegin.onDownloadBegin(statusCode, lengthOfFile, headersFlat);
input = new BufferedInputStream(param.src.openStream(), 8 * 1024);
output = new FileOutputStream(param.dest);
byte data[] = new byte[8 * 1024];
int total = 0;
int count;
while ((count = input.read(data)) != -1) {
if (mAbort.get()) {
break;
}
total += count;
publishProgress(new int[] { lengthOfFile, total });
output.write(data, 0, count);
}
output.flush();
res.statusCode = statusCode;
res.bytesWritten = total;
} finally {
if (output != null) output.close();
if (input != null) input.close();
}
}
protected void stop() {
mAbort.set(true);
}
@Override
protected void onProgressUpdate(int[]... values) {
super.onProgressUpdate(values);
mParam.onDownloadProgress.onDownloadProgress(values[0][0], values[0][1]);
}
protected void onPostExecute(Exception ex) {
}
}

View File

@ -1,5 +1,6 @@
package com.rnfs; package com.rnfs;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.ArrayList; import java.util.ArrayList;
@ -9,6 +10,7 @@ import android.os.AsyncTask;
import android.util.Base64; import android.util.Base64;
import android.content.Context; import android.content.Context;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.SparseArray;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -31,7 +33,6 @@ import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableArray;
import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.core.DeviceEventManagerModule;
public class RNFSManager extends ReactContextBaseJavaModule { public class RNFSManager extends ReactContextBaseJavaModule {
@ -42,6 +43,8 @@ public class RNFSManager extends ReactContextBaseJavaModule {
private static final String NSFileTypeRegular = "NSFileTypeRegular"; private static final String NSFileTypeRegular = "NSFileTypeRegular";
private static final String NSFileTypeDirectory = "NSFileTypeDirectory"; private static final String NSFileTypeDirectory = "NSFileTypeDirectory";
private SparseArray<Downloader> downloaders = new SparseArray<Downloader>();
public RNFSManager(ReactApplicationContext reactContext) { public RNFSManager(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
} }
@ -193,110 +196,72 @@ public class RNFSManager extends ReactContextBaseJavaModule {
URL url = new URL(urlStr); URL url = new URL(urlStr);
DownloadParams params = new DownloadParams(); DownloadParams params = new DownloadParams();
params.src = url; params.src = url;
params.dest = file; params.dest = file;
params.onTaskCompleted = new OnTaskCompleted() {
public void onTaskCompleted(Exception ex) { params.onTaskCompleted = new DownloadParams.OnTaskCompleted() {
if (ex == null) { public void onTaskCompleted(DownloadResult res) {
boolean success = true; if (res.exception == null) {
callback.invoke(null, success, filepath); WritableMap infoMap = Arguments.createMap();
infoMap.putInt("jobId", jobId);
infoMap.putInt("statusCode", res.statusCode);
infoMap.putInt("bytesWritten", res.bytesWritten);
callback.invoke(null, infoMap);
} else { } else {
callback.invoke(makeErrorPayload(ex)); callback.invoke(makeErrorPayload(res.exception));
} }
} }
}; };
params.onDownloadProgress = new OnDownloadProgress() {
public void onDownloadProgress(int statusCode, int contentLength, int bytesWritten) { params.onDownloadBegin = new DownloadParams.OnDownloadBegin() {
public void onDownloadBegin(int statusCode, int contentLength, Map<String, String> headers) {
WritableMap headersMap = Arguments.createMap();
for (Map.Entry<String, String> entry : headers.entrySet()) {
headersMap.putString(entry.getKey(), entry.getValue());
}
WritableMap data = Arguments.createMap(); WritableMap data = Arguments.createMap();
data.putInt("jobId", jobId);
data.putInt("statusCode", statusCode); data.putInt("statusCode", statusCode);
data.putInt("contentLength", contentLength); data.putInt("contentLength", contentLength);
data.putMap("headers", headersMap);
sendEvent(getReactApplicationContext(), "DownloadBegin-" + jobId, data);
}
};
params.onDownloadProgress = new DownloadParams.OnDownloadProgress() {
public void onDownloadProgress(int contentLength, int bytesWritten) {
WritableMap data = Arguments.createMap();
data.putInt("contentLength", contentLength);
data.putInt("bytesWritten", bytesWritten); data.putInt("bytesWritten", bytesWritten);
sendEvent(getReactApplicationContext(), "DownloadProgress-" + jobId, data); sendEvent(getReactApplicationContext(), "DownloadProgress-" + jobId, data);
} }
}; };
DownloadTask downloadTask = new DownloadTask(); Downloader downloader = new Downloader();
downloadTask.execute(params);
downloader.execute(params);
this.downloaders.put(jobId, downloader);
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
callback.invoke(makeErrorPayload(ex)); callback.invoke(makeErrorPayload(ex));
} }
} }
private class DownloadParams { @ReactMethod
public URL src; public void stopDownload(int jobId) {
public File dest; Downloader downloader = this.downloaders.get(jobId);
public OnTaskCompleted onTaskCompleted;
public OnDownloadProgress onDownloadProgress;
}
public interface OnTaskCompleted {
void onTaskCompleted(Exception ex);
}
public interface OnDownloadProgress {
void onDownloadProgress(int statusCode, int contentLength, int bytesWritten);
}
private class DownloadTask extends AsyncTask<DownloadParams, int[], Exception> {
private DownloadParams mParam;
protected Exception doInBackground(DownloadParams... params) {
mParam = params[0];
try {
this.download(mParam);
mParam.onTaskCompleted.onTaskCompleted(null);
} catch (Exception ex) {
mParam.onTaskCompleted.onTaskCompleted(ex);
return ex;
}
return null;
}
private void download(DownloadParams param) throws IOException {
InputStream input = null;
OutputStream output = null;
try {
HttpURLConnection connection = (HttpURLConnection)param.src.openConnection();
connection.setConnectTimeout(5000);
connection.connect();
int statusCode = connection.getResponseCode();
int lengthOfFile = connection.getContentLength();
input = new BufferedInputStream(param.src.openStream(), 8 * 1024);
output = new FileOutputStream(param.dest);
byte data[] = new byte[8 * 1024];
int total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress(new int[] { statusCode, lengthOfFile, total });
output.write(data, 0, count);
}
output.flush();
} finally {
if (output != null) output.close();
if (input != null) input.close();
}
}
@Override
protected void onProgressUpdate(int[]... values) {
super.onProgressUpdate(values);
mParam.onDownloadProgress.onDownloadProgress(values[0][0], values[0][1], values[0][2]);
}
protected void onPostExecute(Exception ex) {
if (downloader != null) {
downloader.stop();
} }
} }

6
jsconfig.json Normal file
View File

@ -0,0 +1,6 @@
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
}
}