[
https://issues.apache.org/jira/browse/CB-2787?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13683354#comment-13683354
]
Gregor Gabriel edited comment on CB-2787 at 10/22/13 8:02 AM:
--------------------------------------------------------------
This is the Java code for =downloader.js=
{code:javascript}
cordova.define("cordova/plugin/downloader",
function(require, exports, module) {
var exec = require("cordova/exec");
var Downloader = function() {};
/**
*
*/
Downloader.prototype.downloadFile = function(fileUrl, params, win, fail) {
function base64Encode(str) {
var CHARS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var out = "", i = 0, len = str.length, c1, c2, c3;
while (i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if (i == len) {
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if (i == len) {
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
out += CHARS.charAt((c2 & 0xF) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
out += CHARS.charAt(c3 & 0x3F);
}
return out;
}
function load_binary_resource(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);
//XHR binary charset opt by Marcus Granado 2006
[http://mgran.blogspot.com]
req.overrideMimeType('text\/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) {
console.log("unable to load " + fileUrl);
fail();
return null;
}
if (req.response === null) {
fail();
return null;
}
return req.response;
}
console.log("start loading " + fileUrl);
var base64File = base64Encode(load_binary_resource(fileUrl));
//Make params hash optional.
if (!fail) win = params;
PhoneGap.exec(win, fail, "Downloader", "downloadFile", [base64File,
params]);
fileUrl = null;
base64File = null;
};
var downloader = new Downloader();
module.exports = downloader;
});
if (!window.plugins) {
window.plugins = {};
}
if (!window.plugins.downloader) {
window.plugins.downloader = cordova.require("cordova/plugin/downloader");
}
{code}
And this is the Java code of Downloader.java
{code:java}
package com.phonegap.plugins.downloader;
import java.io.File;
import java.io.FileOutputStream;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import android.util.Base64;
public class Downloader extends CordovaPlugin {
private CallbackContext context;
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into
JavaScript.
* @return A PluginResult object with a status and message.
*/
@Override
public boolean execute(String action, JSONArray args, CallbackContext
callbackContext) throws JSONException {
this.context = callbackContext;
if (!action.equals("downloadFile")) {
this.context.sendPluginResult(new
PluginResult(PluginResult.Status.INVALID_ACTION, "Plugin Downloader cannot
handle action "+action));
return true;
}
try {
//String fileUrl = args.getString(0);
//byte[] fileContent = Base64.decode(
params.getString("fileUrl"), Base64.DEFAULT );
byte[] fileContent = Base64.decode( args.getString(0),
Base64.DEFAULT );
JSONObject params = args.getJSONObject(1);
String fileName = params.getString("fileName");
String dirName =params.getString("dirName");
Boolean overwrite = params.has("overwrite") ?
params.getBoolean("overwrite") : false;
this.context.sendPluginResult(this.downloadUrl(fileContent, dirName, fileName,
overwrite));
fileContent = null;
params = null;
return true;
} catch (JSONException e) {
e.printStackTrace();
this.context.sendPluginResult(new
PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage()));
return true;
} catch (InterruptedException e) {
e.printStackTrace();
this.context.sendPluginResult(new
PluginResult(PluginResult.Status.ERROR, e.getMessage()));
return true;
}
}
private PluginResult downloadUrl(byte[] fileUrl, String dirName, String
fileName, Boolean overwrite) throws InterruptedException, JSONException {
try {
System.gc();
Log.d("Downloader", "write file " + dirName + "/" +
fileName);
dirName = dirName.replace("file:///", "/");
File dir = new File(dirName);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dirName, fileName);
if (!overwrite && file.exists()) {
Log.d("Downloader", "File already exist");
JSONObject obj = new JSONObject();
obj.put("status", 1);
obj.put("total", 0);
obj.put("file", fileName);
obj.put("dir", dirName);
obj.put("progress", 100);
return new PluginResult(PluginResult.Status.OK,
obj);
}
int progress = 0;
int fileSize = 0;
FileOutputStream fos = null;
try {
// Log.d("Downloader", "Download starts,
connection is open ...");
fos = new FileOutputStream(file);
fos.write(fileUrl);
//fos.close();
//fos = null;
//fileUrl = null;
} catch (Exception e){
Log.e("Downloader", e.getMessage());
} finally {
try {
fos.close();
fos = null;
fileUrl = null;
//Log.d("Downloader","closed input stream
output stream and connection");
JSONObject obj = new JSONObject();
obj.put("status", 1);
obj.put("total", fileSize);
obj.put("file", fileName);
obj.put("dir", dirName);
obj.put("progress", progress);
return new
PluginResult(PluginResult.Status.OK, obj);
} catch ( Exception e) {
fos = null;
fileUrl = null;
Log.w("Downloader","Fehler beim
Aufraeumen - "+e.getStackTrace().toString() );
}
}
//Log.d("Downloader", "Download finished");
JSONObject obj = new JSONObject();
obj.put("status", 1);
obj.put("total", fileSize);
obj.put("file", fileName);
obj.put("dir", dirName);
obj.put("progress", progress);
return new PluginResult(PluginResult.Status.OK, obj);
}
finally {
// do nothing
}
}
}
{code}
the usage ot the plugin is:
{code:javascript}
try {
dir="/a/path/to/a/directory/no/ending/slash";
file="a_file.name";
window.onLoaded=function(){
console.log("do something here, when download
succeded");
}
window.onError=function(){
console.log("do something here, when download
failed");
}
url =
"https://issues.apache.org/jira/secure/projectavatar?pid=12312420&avatarId=15888&size=large";
window.plugins.downloader.downloadFile(
url,
{overwrite: true,
dirName: dir,
fileName: file},
onLoaded,
onError
);
}
catch (e) {
// do some error handling
}
{code}
was (Author: ggabriel):
This is the Java code for =downloader.js=
{code:javascript}
cordova.define("cordova/plugin/downloader",
function(require, exports, module) {
var exec = require("cordova/exec");
var Downloader = function() {};
/**
*
*/
Downloader.prototype.downloadFile = function(fileUrl, params, win, fail) {
function base64Encode(str) {
var CHARS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var out = "", i = 0, len = str.length, c1, c2, c3;
while (i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if (i == len) {
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if (i == len) {
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
out += CHARS.charAt((c2 & 0xF) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
out += CHARS.charAt(c3 & 0x3F);
}
return out;
}
function load_binary_resource(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);
//XHR binary charset opt by Marcus Granado 2006
[http://mgran.blogspot.com]
req.overrideMimeType('text\/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) {
console.log("unable to load " + fileUrl);
fail();
return null;
}
if (req.response === null) {
fail();
return null;
}
return req.response;
}
console.log("start loading " + fileUrl);
var base64File = base64Encode(load_binary_resource(fileUrl));
//Make params hash optional.
if (!fail) win = params;
PhoneGap.exec(win, fail, "Downloader", "downloadFile", [base64File,
params]);
fileUrl = null;
base64File = null;
};
var downloader = new Downloader();
module.exports = downloader;
});
if (!window.plugins) {
window.plugins = {};
}
if (!window.plugins.downloader) {
window.plugins.downloader = cordova.require("cordova/plugin/downloader");
}
{code}
And this is the Java code of Downloader.java
{code:java}
package com.phonegap.plugins.downloader;
import java.io.File;
import java.io.FileOutputStream;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import android.util.Base64;
public class Downloader extends CordovaPlugin {
private CallbackContext context;
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into
JavaScript.
* @return A PluginResult object with a status and message.
*/
@Override
public boolean execute(String action, JSONArray args, CallbackContext
callbackContext) throws JSONException {
this.context = callbackContext;
if (!action.equals("downloadFile")) {
this.context.sendPluginResult(new
PluginResult(PluginResult.Status.INVALID_ACTION, "Plugin Downloader cannot
handle action "+action));
return true;
}
try {
//String fileUrl = args.getString(0);
//byte[] fileContent = Base64.decode(
params.getString("fileUrl"), Base64.DEFAULT );
byte[] fileContent = Base64.decode( args.getString(0),
Base64.DEFAULT );
JSONObject params = args.getJSONObject(1);
String fileName = params.getString("fileName");
String dirName =params.getString("dirName");
Boolean overwrite = params.has("overwrite") ?
params.getBoolean("overwrite") : false;
this.context.sendPluginResult(this.downloadUrl(fileContent, dirName, fileName,
overwrite));
fileContent = null;
params = null;
return true;
} catch (JSONException e) {
e.printStackTrace();
this.context.sendPluginResult(new
PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage()));
return true;
} catch (InterruptedException e) {
e.printStackTrace();
this.context.sendPluginResult(new
PluginResult(PluginResult.Status.ERROR, e.getMessage()));
return true;
}
}
private PluginResult downloadUrl(byte[] fileUrl, String dirName, String
fileName, Boolean overwrite) throws InterruptedException, JSONException {
try {
System.gc();
Log.d("Downloader", "write file " + dirName + "/" +
fileName);
dirName = dirName.replace("file:///", "/");
File dir = new File(dirName);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dirName, fileName);
if (!overwrite && file.exists()) {
Log.d("Downloader", "File already exist");
JSONObject obj = new JSONObject();
obj.put("status", 1);
obj.put("total", 0);
obj.put("file", fileName);
obj.put("dir", dirName);
obj.put("progress", 100);
return new PluginResult(PluginResult.Status.OK,
obj);
}
int progress = 0;
int fileSize = 0;
FileOutputStream fos = null;
try {
// Log.d("Downloader", "Download starts,
connection is open ...");
fos = new FileOutputStream(file);
fos.write(fileUrl);
//fos.close();
//fos = null;
//fileUrl = null;
} catch (Exception e){
Log.e("Downloader", e.getMessage());
} finally {
try {
fos.close();
fos = null;
fileUrl = null;
//Log.d("Downloader","closed input stream
output stream and connection");
JSONObject obj = new JSONObject();
obj.put("status", 1);
obj.put("total", fileSize);
obj.put("file", fileName);
obj.put("dir", dirName);
obj.put("progress", progress);
return new
PluginResult(PluginResult.Status.OK, obj);
} catch ( Exception e) {
fos = null;
fileUrl = null;
Log.w("Downloader","Fehler beim
Aufraeumen - "+e.getStackTrace().toString() );
}
}
//Log.d("Downloader", "Download finished");
JSONObject obj = new JSONObject();
obj.put("status", 1);
obj.put("total", fileSize);
obj.put("file", fileName);
obj.put("dir", dirName);
obj.put("progress", progress);
return new PluginResult(PluginResult.Status.OK, obj);
}
finally {
// do nothing
}
}
}
{code}
the usage ot the plugin is:
{code:javascript}
try {
dir="/a/path/to/a/directory/no/ending/slash";
file"a_file.name";
window.onLoaded=function(){
console.log("do something here, when download
succeded");
}
window.onError=function(){
console.log("do something here, when download
failed");
}
url =
"https://issues.apache.org/jira/secure/projectavatar?pid=12312420&avatarId=15888&size=large";
window.plugins.downloader.downloadFile(
url,
{overwrite: true,
dirName: dir,
fileName: file},
onLoaded,
onError
);
}
catch (e) {
// do some error handling
}
{code}
> PhoneGap FileTransfer.Download more than 300 files
> --------------------------------------------------
>
> Key: CB-2787
> URL: https://issues.apache.org/jira/browse/CB-2787
> Project: Apache Cordova
> Issue Type: Bug
> Components: Android
> Affects Versions: 2.5.0
> Reporter: Cho
> Assignee: Ian Clelland
>
> I had tried to download more than 300 files and then it hit error after that.
> Seems like some IO Connection was not properly closed. I wrote a small code
> to test it out.
> Any idea? How to file a report or get a source of phonegap to check the real
> cause?
> {code}
> var counter = 500;
> function DownloadFile() {
> if (counter == 0) {
> DownloadComplete();
> return;
> }
> var ft = new FileTransfer();
> var downloadUrl = "<source>";
> var dlPath = "<target>"
> ft.download(downloadUrl, dlPath, function(entry) {
> counter--;
> UpdateProgress();
> DownloadFile();
> }, function(error) {
> DownloadFailed();
> }, true);
> }
> {code}
> note: <target> and <source> is alright because it was it failed when the
> counter goes until 300+.
> {code}
> 03-14 08:35:09.706: E/FileTransfer(24867):
> {"target":"<target>","source":"<source>","http_status":200,"code":1}
> 03-14 08:35:09.706: E/FileTransfer(24867): java.io.FileNotFoundException:
> <source>: open failed: EMFILE (Too many open files)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> libcore.io.IoBridge.open(IoBridge.java:416)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> java.io.FileOutputStream.<init>(FileOutputStream.java:88)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> java.io.FileOutputStream.<init>(FileOutputStream.java:73)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> org.apache.cordova.FileTransfer$4.run(FileTransfer.java:685)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> java.lang.Thread.run(Thread.java:856)
> 03-14 08:35:09.706: E/FileTransfer(24867): Caused by:
> libcore.io.ErrnoException: open failed: EMFILE (Too many open files)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> libcore.io.Posix.open(Native Method)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
> 03-14 08:35:09.706: E/FileTransfer(24867): at
> libcore.io.IoBridge.open(IoBridge.java:400)
> 03-14 08:35:09.706: E/FileTransfer(24867): ... 6 more
> {code}
--
This message was sent by Atlassian JIRA
(v6.1#6144)