Return-Path: X-Original-To: apmail-cordova-issues-archive@minotaur.apache.org Delivered-To: apmail-cordova-issues-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 0930D108E5 for ; Tue, 22 Oct 2013 08:04:06 +0000 (UTC) Received: (qmail 76731 invoked by uid 500); 22 Oct 2013 08:04:02 -0000 Delivered-To: apmail-cordova-issues-archive@cordova.apache.org Received: (qmail 76664 invoked by uid 500); 22 Oct 2013 08:04:02 -0000 Mailing-List: contact issues-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list issues@cordova.apache.org Received: (qmail 76478 invoked by uid 99); 22 Oct 2013 08:03:44 -0000 Received: from arcas.apache.org (HELO arcas.apache.org) (140.211.11.28) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 22 Oct 2013 08:03:44 +0000 Date: Tue, 22 Oct 2013 08:03:43 +0000 (UTC) From: "Gregor Gabriel (JIRA)" To: issues@cordova.apache.org Message-ID: In-Reply-To: References: Subject: [jira] [Comment Edited] (CB-2787) PhoneGap FileTransfer.Download more than 300 files MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-JIRA-FingerPrint: 30527f35849b9dde25b450d4833f0394 [ 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:01 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 { url = "https://issues.apache.org/jira/secure/projectavatar?pid=12312420&avatarId=15888&size=large"; window.plugins.downloader.downloadFile( url.replace(/ /g,'%20'), {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 = ""; > var dlPath = "" > ft.download(downloadUrl, dlPath, function(entry) { > counter--; > UpdateProgress(); > DownloadFile(); > }, function(error) { > DownloadFailed(); > }, true); > } > {code} > note: and is alright because it was it failed when the counter goes until 300+. > {code} > 03-14 08:35:09.706: E/FileTransfer(24867): {"target":"","source":"","http_status":200,"code":1} > 03-14 08:35:09.706: E/FileTransfer(24867): java.io.FileNotFoundException: : 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.(FileOutputStream.java:88) > 03-14 08:35:09.706: E/FileTransfer(24867): at java.io.FileOutputStream.(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)