Downloading files to a PhoneGap application - Part 1

This post is more than 2 years old.

For the past week or so I've been looking at file system access and downloads with PhoneGap. Before going any further, I want to warn folks that I'm still a bit fuzzy on the details here. It was a bit of a struggle to get this working right, and I plan to follow this entry up with a look at iOS and also how to get all platforms working right, but for now, consider this a first draft. I also want to give thanks to Simon Mac Donald for his help. Anything right here is thanks to him and anything wrong is my fault.

Ok, with that out of the way. Let's talk about file downloads. A reader pinged me recently to ask about how to support offline PhoneGap applications. Specifically, he wanted to work with images that were remote and make them available to the application when the device was offline. I decided to work on a simple application that would fetch images from a server and store them locally.

I began by looking over the File docs at PhoneGap. This is - for the most part - a wrapper for the W3C File API. I had a real hard time grokking this API. My gut take on it is this:

  • You begin by requesting a file system. This request is either for a persistent or temporary storage. Obviously which you pick depends on what your needs are. For my demo application, I need the persistent storage.
  • What you get back is a file system object. From what I see in the spec, the object contains a few properties, but your primary usage of this is to get a directory entry.
  • Once you have a directory object, you can enumerate files, read them, whatever.

Based on what I learned from Simon, in Android, the place you want to store your files is:

Android/data/X

Where X is the identify of your application. For my demo, this was com.camden.imagedownloaddemo. For the first iteration of my demo, I requested the file system, the directory, and then a list of files:

<!DOCTYPE HTML> <html> <head> <meta name="viewport" content="width=320; user-scalable=no" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>Image Download Demo</title>

<script type="text/javascript" charset="utf-8" src="phonegap-1.3.0.js"></script> <script type="text/javascript" charset="utf-8"> //Global instance of DirectoryEntry for our data var DATADIR;

//Loaded my file system, now let's get a directory entry for where I'll store my crap function onFSSuccess(fileSystem) { fileSystem.root.getDirectory("Android/data/com.camden.imagedownloaddemo",{create:true},gotDir,onError); }

//The directory entry callback function gotDir(d){ DATADIR = d; var reader = DATADIR.createReader(); reader.readEntries(gotFiles,onError); }

//Result of reading my directory function gotFiles(entries) { console.log("The dir has "+entries.length+" entries."); for (var i=0; i<entries.length; i++) { console.log(entries[i].name+' '+entries[i].isDirectory); } }

function onError(e){ console.log("ERROR"); console.log(JSON.stringify(e)); }

function onDeviceReady() { window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, null); }

function init() { document.addEventListener("deviceready", onDeviceReady, true); }
</script>

</head> <body onload="init();" > <h2>Image Download Demo</h2>

<div id="status"></div>

</body> </html>

As everything is async, the code gets a bit complex, but I begin by requesting the file system, requesting the directory (and notice, you can pass an optional argument to automatically create it, which is useful), and then the files.

Ok - so that seemed to work. It was then time to look into the file sync aspects. To keep things simple, my sync logic would just ask a remote server for a list of images. Every image the remote server had that I did not, I downloaded. Obviously this means I can be left with images locally I don'rt need, but I wanted to keep things as basic as possible. Here's the new version:

<!DOCTYPE HTML> <html> <head> <meta name="viewport" content="width=320; user-scalable=no" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>Image Download Demo</title> <script type="text/javascript" charset="utf-8" src="jquery.min.js"></script> <script type="text/javascript" charset="utf-8" src="phonegap-1.3.0.js"></script> <script type="text/javascript" charset="utf-8"> //Global instance of DirectoryEntry for our data var DATADIR; var knownfiles = [];

//Loaded my file system, now let's get a directory entry for where I'll store my crap function onFSSuccess(fileSystem) { fileSystem.root.getDirectory("Android/data/com.camden.imagedownloaddemo",{create:true},gotDir,onError); }

//The directory entry callback function gotDir(d){ console.log("got dir"); DATADIR = d; var reader = DATADIR.createReader(); reader.readEntries(function(d){ gotFiles(d); appReady(); },onError); }

//Result of reading my directory function gotFiles(entries) { console.log("The dir has "+entries.length+" entries."); for (var i=0; i<entries.length; i++) { console.log(entries[i].name+' dir? '+entries[i].isDirectory); knownfiles.push(entries[i].name); renderPicture(entries[i].fullPath); } }

function renderPicture(path){ $("#photos").append("<img src='file://"+path+"'>"); console.log("<img src='file://"+path+"'>"); }

function onError(e){ console.log("ERROR"); console.log(JSON.stringify(e)); }

function onDeviceReady() { //what do we have in cache already? $("#status").html("Checking your local cache...."); window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, null); }

function appReady(){ $("#status").html("Ready to check remote files..."); $.get("http://www.raymondcamden.com/demos/2012/jan/17/imagelister.cfc?method=listimages", {}, function(res) { if (res.length > 0) { $("#status").html("Going to sync some images..."); for (var i = 0; i < res.length; i++) { if (knownfiles.indexOf(res[i]) == -1) { console.log("need to download " + res[i]); var ft = new FileTransfer(); var dlPath = DATADIR.fullPath + "/" + res[i]; console.log("downloading crap to " + dlPath); ft.download("http://www.raymondcamden.com/demos/2012/jan/17/" + escape(res[i]), dlPath, function(){ renderPicture(dlPath); console.log("Successful download"); }, onError); } } } $("#status").html(""); }, "json");

}

function init() { document.addEventListener("deviceready", onDeviceReady, true); }
</script>
<style> img { max-width: 200px; } </style> </head> <body onload="init();" > <h2>Image Download Demo</h2>

<div id="status"></div>

<div id="photos"></div>

</body> </html>

Ok, it's a bit much, but let's work through the various events. You can still see the file system request as well as the directory list. I do two new things now once I have the files. I remember them (storing them in knownfiles), and I render them using a simple utility function. Yes - you can pass a path to an image source and it works just fine.

Now - take a look at appReady. This handles my remote call. I'll share the ColdFusion code if folks want, but all it's doing is returning a JSON-encoded array of images. For each result, I see if I already have it, and if not, use the download method of the FileTransfer object. Note: One of my images had a space in the file name. This causes all kinds of problems until I simply escaped it:

ft.download("http://www.raymondcamden.com/demos/2012/jan/17/" + escape(res[i]), dlPath, function(){

Here's a quick screen shot. Obviously it is static so you can't see it working, but in my testing, when I pushed up a new image remotely, and reran the application, it immediately noticed it was missing one and grabbed it.

So - what's next? As I said, this is currently Android specific, and that's bad. I'm next going to test on iOS, and then get one application that can handle both. Also, I didn't actually bother checking to see if the device was online. That would be trivial via the Connection API and should be done. (I'll remember to do it for the final, "combined" demo.)

Does this make sense? Any questions?

Edit on January 20, 2012: Note that I made a mistake in my fileTransfer callback. I talk about this mistake here, but the critical fix is right here:

var dlPath = DATADIR.fullPath + "/" + res[i]; console.log("downloading crap to " + dlPath); ft.download("http://www.raymondcamden.com/demos/2012/jan/17/" + escape(res[i]), dlPath, function(e){ renderPicture(e.fullPath); console.log("Successful download of "+e.fullPath); }, onError);

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Philip Senn posted on 1/20/2012 at 10:20 AM

I Thought the cache.manifest was for storing offline files like images. This is all completely new info for me. Thanks for the code!

Comment 2 by Raymond Camden posted on 1/20/2012 at 4:28 PM

HTML5 Offline Storage could create a similar effect. In this case though the PhoneGap is local HTML, so you can't really use it. This approach could also be made much more complex - for example, it could download large images only when your connection is Wifi as opposed to cell.

Comment 3 by Michel Morelli posted on 1/21/2012 at 7:07 AM

Thanks for this post. I need it.

M.

Comment 4 by Sumit Verma posted on 1/21/2012 at 10:39 AM

Thanks Ray! Works fine on iOS without any issues. Filetransfer.download() was the key. Somehow I didn't see it in the PhoneGap API doc.

I'm downloading all the images on app load and then replacing the src of the image on page load.

/* change images to local on page load */
function renderLocalImage(){
var contentObj = $("#pageContent");
$('img',contentObj).each(function(index){
var src = $(this).attr('src');
var srcArray = src.split('/');
var imageName = srcArray[srcArray.length-1];
var localImage = 'file://' + imageDir.fullPath + '/' + imageName;
$(this).attr('src',localImage);
});
}

Comment 5 by Raymond Camden posted on 1/21/2012 at 7:28 PM

You missed it because it isn't listed on top. I missed it too at first. Sorry it took so long (for others, Sumit was the guy who kinda started me on this).

Comment 6 by Sumit Verma posted on 1/22/2012 at 1:16 PM

The funny thing is it works perfectly in iOS, but I can't get it to work on Android! The getDirectory method keeps throwing error 12 (FileError.PATH_EXISTS_ERR = 12).

Also, on ICS (4.0.3) I can't even get open database to work. That throws:

I/SqliteDatabaseCpp(8031): sqlite returned: error code = 14, msg = cannot open file at line 27701 of [8609a15dfa], db=/data/data/com.ten24.androidtest/databases/webview.db

Comment 7 by Raymond Camden posted on 1/22/2012 at 8:25 PM

Sumit, one thing I ran into was not being able to write to my SD when my phone was in Disk Mode. Is that something you have enabled? If you try w/o the path, or make the path match what I did in part 2, does that work?

Although - it sounds like maybe your apps can't write at all - since you can't even open a db. May be worth while posting to the Google Groups and seeing if they know something.

Comment 8 by Sumit Verma posted on 1/23/2012 at 9:10 AM

Ok, so I got it to work on Android 2.3.3 but 4.0.3 (ASUS Prime) throws connection error on download.

Few points:

- The database error seems to be always there but it still works.

- The File error is because getDirectory('xxx',{create:true}) throws error if the directory exists. getDirectory('xxx',{create:true,exclusive:false}) returns the directory if it exists.

- Also nested directory creation doesn't work. You can only create one folder at a time.

- In order to use file system make sure phone is _not_ connected in USB mode. Because connecting it in USB mode un-mounts the SD card. In DroidX the come is called "Charge Only".

I have to say, iOS development seems much more easier (trouble free) than Andriod.

Comment 9 by Raymond Camden posted on 1/23/2012 at 4:45 PM

Wow, this is truly surprising because you are seeing pretty much 100% the opposite of me:

1) No db errors for me.

2) When my directory existed, I never got an error.

3) Didn't test nested.

4) For me, my phone was in Charge Mode and it worked ok. It didn't work in disk mode.

Comment 10 by Sumit Verma posted on 1/23/2012 at 7:31 PM

Ray, Can you try your code on 4.0.3 AVD and see if you are getting error as well?

Found this thread about DB error where Simon says he has always seen it and to just ignore it:
http://groups.google.com/gr...

Comment 11 by Simon MacDonald posted on 1/24/2012 at 8:51 PM

Yeah, you can safely ignore the DB error as it is just noise. I've grabbed Ray's code and tried it in a 4.0 emulator and it fails. I think, don't quote me yet, but I think it is a bug in Android 4.0 as it is failing to open a url connection. I'm going to raise a bug on Jira and do some more investigation.

Comment 12 by Sumit Verma posted on 1/24/2012 at 8:54 PM

Thanks Simon.

Comment 13 by Raymond Camden posted on 1/24/2012 at 9:02 PM

Thanks for testing this Simon (didn't get a chance to yet). Please post back when you hear more.

Comment 14 by Simon MacDonald posted on 1/24/2012 at 9:28 PM

The download method in FileTransfer was setting the HttpURLConnection.setDoOutput to true. On Android 4.0 this would force the connection to be a POST when what we actually want is a GET.

I removed the offending line and tested the fix in 2.1, 2.2, 2.3 and 4.0. The fix is checked in for PhoneGap 1.4 which should be out early next week.

Comment 15 by Raymond Camden posted on 1/24/2012 at 9:41 PM

Damn. I'm pretty disappointed in PhoneGap. I mean - this took - what - 37 minutes? How can I have faith in a framework that needs so much time to correct issues.

;)

Thanks Simon - this is great to hear!

Comment 16 by Sumit Verma posted on 1/24/2012 at 9:49 PM

Wow! Simon, now you have set a very high expectation for future fixes. Haha...

Thanks a lot for the quick response. Is 1.4 available to play with?

Comment 17 by Simon MacDonald posted on 1/24/2012 at 11:08 PM

Well fixes won't always come that fast but when I have a reproduction scenario and a stack trace it makes it a lot easier to track down and fix. If only all bugs had this much information.

You can always grab the latest code from:

https://git-wip-us.apache.o...

or the regularly updated mirror on github:

https://github.com/apache/i...

You won't have long to wait for the official 1.4 though as it should be out on Monday.

Comment 18 by Alex posted on 2/26/2012 at 7:35 PM

Thanks for this fantastic "tutorial" it saved me lots of time and a project i am working on.

I ran in a little problem, you compare the known files and the files downloadable files, do you have an idea how to identify a file tha needs to be updated?
What i do is i update the data in my app (download a JSON file) but only want to download the file if it has been changed since the last update.

regards
Alex

Comment 19 by Raymond Camden posted on 2/26/2012 at 8:48 PM

Alex, unfortunately, you can't (afaik!) get the file metadata on a remote file. Using a server side solution (like ColdFusion, PHP, Ruby, etc), I'd build a service that returns a list of files and their updated date. This sounds more complex (and I guess any change would be), but in theory it wouldn't be that much harder.

On the client side (your PhoneGap app), your code requests this server, and then compares it to the local directory.

Comment 20 by Alex posted on 2/28/2012 at 12:12 AM

Raymond, thx for replying.
I found a solution. I request a php page which returns an json containing a date.
I create an empty file on the phone and name it like the date.
Next time i check for the "datefile" if it exists i am not updating if it doesnt i download the update.

Thanks for pointing me in the right direction.

Comment 21 by Raymond Camden posted on 2/28/2012 at 12:17 AM

If you just want to store that date, why bother using a file? Just use localStorage.

Comment 22 by Alex posted on 2/29/2012 at 10:27 PM

Thx again for pointing me in the right direction.
Completely forgot about localStorage.

If i may i have one last question (as i am not very familiar with jquery).
I use your code to download a file but it seems that the app "starts" before the file is complete.
Result, old data is shown instead of the new on. Do you have any idea how to tell the app to wait for the download to complete and display a short message?

thx for all your help

Comment 23 by Raymond Camden posted on 2/29/2012 at 10:30 PM

The download api has a success call back, that's where I call renderPicture. So your app should start off with a "Please stand by" type message, and in your success callback, you can simply remove that and proceed.

Comment 24 by Alex posted on 3/1/2012 at 2:32 PM

Okay, my last question was not very precise ;-)
I use sencha touch for my app and the app starts before the download ist completed.
My idea is that i load the index.html and do all the update stuff. When all the updates are done (or not if the user has no internet connection) i want to move on to another page where i start the actual app.
In your example you would just download all the pictures and start the display of the pictures after all the downloading is done.

Comment 25 by Raymond Camden posted on 3/1/2012 at 4:53 PM

So instead of running renderPicture like I did, you would move the user to the ohter page. If you are using something like jQuery Mobile, there is an API for that. Otherwise, document.location should work.

Comment 26 by Alex posted on 3/1/2012 at 5:27 PM

Ahhh...
Ok, i am downloading multiple files. So what i could do is check if it is the last file and then move the user to the next page.

ft.download("http://my_download_path/" + escape(res[x]), dlPath, function(){
if ($x == res.length){
document.loction = 'app.html';
}
}, onError);

In the onError function i put also a location change if something goes wrong during download.

And before i even start downloading i check with
if (navigator.online)....
that an internet connection exists.

Ok..thx again for your patience and your help. Hope this was the last time i had to bother you :-)

Comment 27 by Raymond Camden posted on 3/1/2012 at 5:32 PM

One thing to remember though. If you are downloading N files, then it is possible that when the callback for the Nth one fires, that an earlier one is still working. That's the nature of asynch operations.

jQuery 1.5 added easier support for grouping together such tasks such that you can "do x when all is done". This feature is based on a feature called Deferreds. I'd do a bit of reading up on it. (Personally, I still struggle with the syntax.)

For my app I didn't have to worry about it.

I should force myself to update the app to use it. :)

Comment 28 by Alex posted on 3/1/2012 at 6:19 PM

Jep, that is exactly what happend...
Too bad.
Is there a way to download files NOT asynch?
Get list of files from the JSON and than download one after the other?
As far as i understand, the asynch method should prevent the app from making the user wait till all is loaded.
But when updating data i will tell the user that the app is updating and wait for the update to finish.

Comment 29 by Raymond Camden posted on 3/1/2012 at 7:53 PM

Nope, it must be asynch.

I'd really check the jQuery docs. I know I made it sound kind of scary, but it DOES work, and is very doable. It's just a topic I'm having a hard time wrapping my head around.

Comment 30 by Alex posted on 3/1/2012 at 8:45 PM

I had a first look at the jQuery docu but i think i will have to look for a few more examples.
Maybe i should first try to fully understand how all those callback functions work ;-)
I'll give it a try.

Comment 31 by Sumit Verma posted on 3/1/2012 at 8:54 PM

Here is how you can make is synchronous:

PS: there is some extra code in here that you can remove, but you will get the idea.

/* download all the images */
function downloadImages(data){
var imageList = data.IMAGELIST;
var imagePath = data.IMAGEPATH;
var imageName = "";
imageData.imagePath = imagePath;
imageData.imageList = [];
var colMap = new Object();
//create column map
for (var i = 0; i < imageList.COLUMNS.length; i++) {
colMap[imageList.COLUMNS[i]] = i;
}
for (var i = 0; i < imageList.DATA.length; i++) {
imageName = imageList.DATA[i][colMap["NAME"]];
imageData.imageList.push(imageName);
}
consoleLog('Total images to download: ' + imageData.imageList.length);
downloadImage();
}

/* download image syncronously */
function downloadImage(){
if(imageData.imageList.length > 0){
var imageName = imageData.imageList[0];
var imagePath = imageData.imagePath;
consoleLog('Start file download: ' + imagePath + imageName);
//check if file exists, pass the function as new closure, so the variable is preserved
imageDir.getFile(imageName, {create: false}, successImageExists, (function(name, path){
return function(){
failImageExists(name, path)
}
})(imageName, imagePath));
}
}

/* image exists */
function successImageExists(file){
consoleLog('Image Exists: ' + file.name);
updateImageDownloadProgress();
}

/* image doesn't exists */
function failImageExists(imageName,imagePath){
consoleLog('image does not Exists');
//start download
consoleLog('Download Image To: ' + imageDir.fullPath + '/' + imageName);
ft.download(imagePath + escape(imageName), imageDir.fullPath + '/' + imageName, onImageDownload, onImageDownloadFail);
}

/* on image download fail */
function onImageDownloadFail(error){
consoleLog('image not downloaded. Error: ' + JSON.stringify(error));
updateImageDownloadProgress();
}

/* on image download success */
function onImageDownload(image){
updateImageDownloadProgress();
}

/* update image download progress */
function updateImageDownloadProgress(){
imageData.imageList = imageData.imageList.slice(1);
consoleLog('images left to download: ' + imageData.imageList.length);
if(imageData.imageList.length == 0){
imagesDownloaded = true;
} else {
downloadImage();
}
}

Comment 32 by Raymond Camden posted on 3/1/2012 at 9:12 PM

Sumit, please please please use pasteBin in the future. :)

Comment 33 by Sumit Verma posted on 3/1/2012 at 10:55 PM

Sure, sorry :)

Comment 34 by Sumit Verma posted on 3/1/2012 at 10:58 PM

You can delete the earlier comment, if you want. Here is the paste bin version:

http://pastebin.com/kNvF166F

Comment 35 by Raymond Camden posted on 3/1/2012 at 11:02 PM

It's ok.

But... I will say this. Your implementation is not one I agree with. It looks like it would work, but you really should look at deferreds. It handles this much more elegantly.

If anything, you've convinced me to try to find time to update my demo. ;)

Comment 36 by Sumit Verma posted on 3/1/2012 at 11:19 PM

I agree, it can be done more elegantly using deferreds, but I didn't had time to play with that and this was a quick and easy solution (and it does work ;))...

Comment 37 by Alex posted on 3/1/2012 at 11:21 PM

Well maybe deferred is more elegant, but Sumit gave me good idea for a workaround if i am not able to get deferred to work.

If i get deferred to work i will let you know and post my solution.

Sumit, thx for giving me an good idea for a workaround.
Thx to both of you for the great support :-)

Comment 38 by Sumit Verma posted on 3/1/2012 at 11:27 PM

Another point wanted to mention is, if you are downloading lot of file that are large in size, synchronous download is your only option. The Async starts failing because I guess its too many threads for the device to handle (at-least in iOS).

Comment 39 by Raymond Camden posted on 3/1/2012 at 11:28 PM

Yeah, sorry if I sounded too negative. I can promise you that Sumit's working version is better than the one I haven't written yet. ;)

Comment 40 by Alex posted on 3/5/2012 at 7:22 PM

@Sumit, ist possible to see the rest of your code?
I guess you set some of the variables used in your code outside.
e.g.
downloadImages(data)v <= what does Data contain
imageDir. <= where and how is this defined
imageData <= same as above

Would be great if you could show me how you modified raymonds code to work with your example.
Sorry for bothering you again :-)

Thx for your great help.

Comment 41 by Sumit Verma posted on 3/5/2012 at 9:21 PM

Alex,

Data is a struct returned from CF.

data.imagePath = "http://domain.com/path/to/i..."
data.imageList = list of image names (converted to JSON from query by CF)

Imagelist comes from cfdirectory.

imageDir is the reference to image directory in FileSystem. Same as DATADIR in Ray's example.

imageData is a global JS variable to hold the image name array, as files are downloaded image names are removed from this array.

HTH,
Sumit

Comment 42 by Raymond Camden posted on 3/5/2012 at 9:24 PM

As a side note, Sumit, I tried your technique in a different context (basically I needed to single thread N ajax calls) and it worked well. Thanks.

Comment 43 by Alex posted on 3/5/2012 at 10:17 PM

Sumit,

so data.imageList is almost the same as Raymonds
$.get("http://www.raymondcamden.co...", {}, function(res) {
....
}, "json");

So what i could do is take your function downloadImages() and change it to:

function downloadImages(data){
var imagePath = 'http://path_to_my_files/';
$.get("http://path_to_my_JSON/json.php", {}, function(res) {

var imageName = "";
imageData.imagePath = imagePath;
imageData.imageList = [];
var colMap = new Object();
//create column map
for (var i = 0; i < res.COLUMNS.length; i++) {
colMap[res.COLUMNS[i]] = i;
}
for (var i = 0; i < res.DATA.length; i++) {
imageName = res.DATA[i][colMap["NAME"]];
imageData.imageList.push(imageName);
}
consoleLog('Total images to download: ' + imageData.imageList.length);
}, "json");
downloadImage();
}

My webpage (json.php) returns something like this:
["files.json","data.json","dates.json"] (the files i want to download)
Ist this what your column mapper expects?

Btw. just to give you a short explaination why i ask so much instead of trying it all by myself.
This is my first try with HTML5, javascript and phonegap. I promised a friend to help him with a little non commercial app and i will be on holiday by the end of the week for 4 weeks ;-)

Comment 44 by Sumit Verma posted on 3/5/2012 at 10:32 PM

Yes to all except column mapper. You don't need it if you are getting the data as array of file names. You can just set imageData.imageList to that array. I'm using column mapper because I'm converting a query object (with image name and date modified) in CF to JSON.

Comment 45 by Alex posted on 3/5/2012 at 11:11 PM

I gave it try:

http://pastebin.com/gZQXL8P3

but it breaks after
..console.log("Start downloading...");
within downloadImages()

Btw. the download process is still triggerd by the init() script from Rays example. Is this the best way to start the download and make the page "wait" until the download is finished and then move on or relocate to another page?

Comment 46 by Alex posted on 3/6/2012 at 1:08 AM

Oh my god, that was really stupid...

i used console.Log instead of console.log....

Comment 47 by Raymond Camden posted on 3/6/2012 at 10:01 AM

Alex - I do that a lot myself. Don't feel bad. :)

Comment 48 by Shailesh posted on 3/7/2012 at 5:01 PM

Hi, http://www.raymondcamden.co..."
in the code above what do you mean by : imagelister.cfc?method=listimages

I am going to use this code but only stuck at above line. i am new to phonegap and i9 need to synchronize images from remote server. please help.

Comment 49 by Raymond Camden posted on 3/7/2012 at 6:17 PM

That is a file on my ColdFusion server. It could be any technology - PHP, Ruby, whatever. For me, I used ColdFusion.

Comment 50 by Jerry Zhu posted on 3/20/2012 at 7:54 PM

Hello Raymond
Good tutorial, very helpful. However, I still got error while file downloading. I copied all code to my project, when I run it on my real mobile (android 2.3.5), the error came out. It says:
-----
03-20 23:51:34.863: E/FileTransfer(16025): Error while downloading
03-20 23:51:34.863: E/FileTransfer(16025): java.io.IOException: Error while downloading
03-20 23:51:34.863: E/FileTransfer(16025): at org.apache.cordova.FileTran...(FileTransfer.java:429)
03-20 23:51:34.863: E/FileTransfer(16025): at org.apache.cordova.FileTransfer.execute(FileTransfer.java:102)
03-20 23:51:34.863: E/FileTransfer(16025): at org.apache.cordova.api.PluginManager$1.run(PluginManager.java:150)
03-20 23:51:34.863: E/FileTransfer(16025): at java.lang.Thread.run(Thread.java:1019)
-----
Any thoughts?

Comment 51 by Raymond Camden posted on 3/21/2012 at 12:05 AM

What are you downloading? Can you share all the code? (Via pastebin of course.)

Comment 52 by Jerry Zhu posted on 3/21/2012 at 6:45 AM

The codes are copied from your post in fact..So the images it downloaded are same from the json. The json can interpret currectly not the issues happened via downloading. Tricky..

Comment 53 by Raymond Camden posted on 3/21/2012 at 2:34 PM

So to be clear, it dies on the actual file download? You see all the other (previous) status messages as the app starts up?

Comment 54 by Jerry Zhu posted on 3/21/2012 at 6:28 PM

Yes, I can see correct JOSN response and "starting downloading xxx" in console. But the images weren't downloaded at all. I have checked the folder created by this app, nothing inside. Interesting problem... Drive me crazy..

Comment 55 by Raymond Camden posted on 3/22/2012 at 4:45 AM

What I'd recommend then is adding some console.log messages. You need to find out _exactly_ where it breaks.

Comment 56 by Jerry Zhu posted on 3/23/2012 at 7:09 PM

Could I send email to you with screenshots? I'm using phonegap 1.5.

Comment 57 by Raymond Camden posted on 3/23/2012 at 7:39 PM

Screenshots won't help. Try what I recommended - using console.log to try to diagnose where exactly it's failing.

Comment 58 by Shailesh posted on 3/30/2012 at 2:07 PM

Hi, can i get the sample code for imagelister.cfc?method=listimages File so i come to know how exctly i should write code in my PHP file?

Comment 59 by Shailesh posted on 3/30/2012 at 2:33 PM

Also i have tried your server to check the code and try to download the images but only get the below message and then nothing happen...

Checking your local cache....

Comment 60 by Raymond Camden posted on 3/30/2012 at 3:26 PM

Shailesh: All it does is list the directory and create an array of file names. As to your error, best I can suggest is what I told Jerry, to try some console messages.

I've gotten reports that 1.5 may be an issue. Sometime today/this weekend I'll try to replicate.

Comment 61 by AJHino posted on 4/11/2012 at 8:30 PM

I'm using a similar approach to $,get a JSON object containing a list of various URL resources to store down to my app. The files are put into various directories, mirroring the directory structure of the web server.

Is there a way to set the gotFIles() function to do a scandir type of action so it can also check all subdirectories for any matched files?

Comment 62 by Raymond Camden posted on 4/11/2012 at 10:12 PM

Well, anything should be possible - you can write code to recursively scan folders. I don't know of any examples of that yet though.

Comment 63 by AJHino posted on 4/12/2012 at 2:38 AM

I was hoping there would be some file reader method that would recursively scan folders. I guess a home brew is in order on this one.

One other issue I have run into is finding a way to trigger an "all files finished downloading" event. I have tried inserting a callback function after the .get function, in the get function if the last resource has been reached, and a handful of other places. It seems that the callback function will execute after the JSON object is scanned, but before the actual download process completes. I'm looking for something similar to the FileWriter's property of readyState that can indicate a "DONE" state. I have really had a hard time finding any documentation on the File Transfer download method.

Comment 64 by Raymond Camden posted on 4/14/2012 at 1:54 AM

If you've got N sets of transfers, you need a way to handle all of those asyncs being done. Afaik, jQuery Deferreds provide a way of doing this, but I'm still wrapping my head around them.

Comment 65 by Shailesh posted on 5/4/2012 at 2:39 PM

Hi,

I have downloaded image in emulator and its works fine but somehow on real device it is not downloading the images. don't where it stuck... ?? any idea about that?

Thanks in advance.

Comment 66 by Raymond Camden posted on 5/4/2012 at 6:09 PM

I recommend making liberal use of console.log to try and diagnose where it is failing. You should be able to do this and see the failure.

Comment 67 by Gavriel posted on 5/17/2012 at 12:47 AM

Sumit, did you find a fix to the FileError.PATH_EXISTS_ERR? I'm running my code on Android via build.phonegap.com (phonegap 1.6.1) and I get this when I try to open my app's main directory that already exsists: fileSystem.root.getDirectory("com.example.myapp", {create: true, exclusive: false}, ...)

Comment 68 by Gavriel posted on 5/17/2012 at 12:49 AM

Forgot to mention that I run it on Android 2.3.7

Comment 69 by Sumit Verma posted on 5/17/2012 at 12:55 AM

@Gavriel

Yes, please see my earlier comment.

http://www.raymondcamden.co...

Sumit

Comment 70 by Gavriel posted on 5/17/2012 at 2:03 AM

I already had the exclusive: false. And it also turned out that the directory wasn't there. So it is probably a bug in build.phonegap.com's version. When I create the directory (for example with my app, but built in Eclipse with cordova 1.7.2, then it creates the directory) then it can open it.

However now my next problem: FileTransfer.download gives me ABORT_ERR. Any idea why? I'm trying to download a file to the TEMPORARY filesystem

Comment 71 by Gavriel posted on 5/17/2012 at 3:18 AM

adding to config.xml:
<feature name="http://api.phonegap.com/1.0..."/>
<access origin="https://mydomain.com" />
fixed it

Comment 72 by Huzoor Bux Panhwar posted on 5/31/2012 at 3:03 PM

Same Code dosen't work on my android 2.2 return me below message.

05-31 16:58:32.972: DEBUG/SntpClient(59): request time failed: java.net.SocketException: Address family not supported by protocol

Please help me where i am making mistake i am using phonegap 1.7.0 version which i have changed in the code this code is created on 1.3.0.

Comment 73 by Raymond Camden posted on 5/31/2012 at 5:19 PM

When does this error get thrown?

Comment 74 by Huzoor Bux Panhwar posted on 5/31/2012 at 7:52 PM

When i am running my application it thrown this error.

Comment 75 by Raymond Camden posted on 5/31/2012 at 7:54 PM

Right, but what you need to do is try to determine when in the application flow. Notice the console messages? They would give you an idea of when things fail. If you do not know how to read the console, see my other blog post on the topic.

Comment 76 by Huzoor Bux Panhwar posted on 5/31/2012 at 8:03 PM

I got this error from console log.

Comment 77 by Raymond Camden posted on 5/31/2012 at 8:06 PM

Right... but my point is this. Use the console to figure out _when_ the error occurs. What part of the process defined in the application.

Comment 78 by Huzoor Bux Panhwar posted on 6/7/2012 at 3:06 PM

Hey Raymond,

getting this errors in debugger....

06-07 17:04:23.250: DEBUG/CordovaLog(560): file:///android_asset/www/index.html: Line 47 : ERROR

06-07 17:04:23.250: INFO/Web Console(560): ERROR at file:///android_asset/www/index.html:47

06-07 17:04:23.250: DEBUG/CordovaLog(560): {"code":12}

06-07 17:04:23.250: DEBUG/CordovaLog(560): file:///android_asset/www/index.html: Line 48 : {"code":12}

06-07 17:04:23.250: INFO/Web Console(560): {"code":12} at file:///android_asset/www/index.html:48

06-07 17:04:28.110: DEBUG/dalvikvm(251): GC_EXPLICIT freed 160 objects / 11848 bytes in 258ms

and line number 47 and 48 is

function onError(e){
47=> console.log("ERROR");
48=> console.log(JSON.stringify(e));

}

Comment 79 by Raymond Camden posted on 6/7/2012 at 5:31 PM

You still aren't hearing me though. Do you understand that when you do console.log("X"), it prints it out to your console (and in your case, that is the debugger). My point to you is that you should add messages to the code so you can see -when- this error is thrown.

For example, you can put one in onDeviceReady, you can put one in appReady. You can put one before the $.get, in the closure, etc.

Your job here is to find out -when- error code 12 was thrown. I can't tell you this from here.

Comment 80 by Huzoor Bux Panhwar posted on 6/8/2012 at 1:49 PM

I have done this problem is because of whitelist so i have resolved this.

Now i need some thing like live progress in that page which show how much work left.

Comment 81 by Huzoor Bux Panhwar posted on 6/9/2012 at 4:30 PM

Hey Raymond,

Thanks for this great post.

Now i am confused that how can i create nested directory like this.

fileSystem.root.getDirectory("Android/data/com.phonegap.myapp/dir_one/dir_two/",{create:true},gotDir,onError);

and i need to make 3 directories always in each case in my script any solution.

Comment 82 by Raymond Camden posted on 6/9/2012 at 5:25 PM

Unfortunately, you have to do it step by step. So to make /a/b/c, you have to make a (if you need to), then b (if you need to), and so on. For more details, see this excellent article:

http://www.html5rocks.com/e...

Comment 83 by Kenji posted on 6/26/2012 at 6:14 AM

Hi Ray,

Just wondering, could you see a modified version of this code working for grabbing videos from a host on to the local?

Could you file size issues being an issue

Comment 84 by Raymond Camden posted on 6/26/2012 at 5:32 PM

Kenji: To your first paragraph, it is no different. It should just work. To your second paragraph, I'm not quite sure I get what you are syaing.

Comment 85 by Imas posted on 7/3/2012 at 12:47 PM

Hello...
I am beginner on mobile apps, especially phonegap.
I just try the script above, but it goes error...

07-03 15:46:29.409: E/Web Console(811): Uncaught TypeError: Object [object Object] has no method 'download' at file:///android_asset/www/index.html:67

the line is :
ft.download("http://www.raymondcamden.co..." + escape(res[i]), dlPath, function(){....

what should I do?

Comment 86 by Raymond Camden posted on 7/3/2012 at 9:15 PM

Add a quick console.dir(ft) and see if ft is a proper object. It is supposed to be an instance of FileTransfer, which supports a download method.

Comment 87 by Imas posted on 7/4/2012 at 11:48 AM

Hi it's me again...
I just change the version of phonegap.jar and .js with the newest version.. and ft.download doesn't error anymore...
but, i got another one -_-, it said:

Could not find class 'android.webkit.WebResourceResponse', referenced from method org.apache.cordova.CordovaWebViewClient.generateWebResourceResponse

Comment 88 by Raymond Camden posted on 7/4/2012 at 5:33 PM

That's a new one for me. I'd suggest the PhoneGap Google group. Sorry.

Comment 89 by AMet posted on 7/18/2012 at 1:18 PM

Hey Raymond.
Looks like that your function renderPicture(path){
41 $("#photos").append("<img src='file://"+path+"'>");
42 console.log("<img src='file://"+path+"'>");
43}
Doesn't need file:// anymore, I defently just ran this, phonegap 1.9 + jqm. and it was included in path.

Comment 90 by Raymond Camden posted on 7/18/2012 at 7:35 PM

Thanks AMet.

Comment 91 by julee posted on 7/18/2012 at 8:54 PM

Hi, Ray & Imas: Don't know if Imas is still looking for information regarding that error, but I don't think it's of consequence. I came across this https://issues.apache.org/j....

Comment 92 by Jane posted on 7/26/2012 at 5:04 AM

Hi, umm T.T
When I run the emulator, error has occurred.
Error -> code: 12
I was working on android 2.3 and 4.0
but I don't know that
When I work with my cell phone, there was no error.

Comment 93 by Raymond Camden posted on 7/26/2012 at 6:43 PM

I'm having a hard time parsing your comment. You say it works on a real device but not the emulator?

Comment 94 by Jane posted on 7/27/2012 at 3:27 AM

Yes
code dosen't work on emulator.
The getDirectory method keeps throwing error.
error code is 12

Comment 95 by Julius posted on 7/27/2012 at 10:32 AM

Hello,

I tried and I get "401 Unauthorized" on ft.download() method call.

Thanks !!

Comment 96 by Julius posted on 7/27/2012 at 10:57 AM

Hi again,

I had forgotten to add the test url on my config.xml as a whitelist

AndroidProject/res/xml/config.xml

Thanks a lot for your awesome piece of code ;-)

Julius

Comment 97 by Raymond Camden posted on 7/27/2012 at 2:57 PM

Ah so it is all good now?

Comment 98 by Abraham posted on 8/3/2012 at 3:26 AM

I have weird problem testing on Android Device,

It stops on the Directory reader event, it doesn't show any error o success... it just stops and nothing happens.

Any idea?

Comment 99 by Raymond Camden posted on 8/3/2012 at 6:29 PM

Best I can recommend is using the console to try to track down where things are failing.

Comment 100 by Abraham posted on 8/4/2012 at 1:21 AM

got it working, I read about the console a few minutes later of post, tnx!

Comment 101 by yanezricardo posted on 10/11/2012 at 3:58 AM

Hello, could you tell me who is
"imagelister.cfc?method=listimages"? at URL: ("http://www.raymondcamden.co...")

I need to do something similar to read a list of images from my dropbox folder.

Thanks in advance!

Comment 102 by Raymond Camden posted on 10/11/2012 at 5:14 AM

It's ColdFusion code that scans a directory for images and returns a list of these files as a JSON-encoded array. It's no more than 5 or so lines of code.

Comment 103 by yanezricardo posted on 10/11/2012 at 4:05 PM

Could you please give me a snippet? or tell me how to look for information about creating that file and please excuse the audacity!

Comment 104 by Raymond Camden posted on 10/11/2012 at 4:13 PM

To be clear, this is like 3 lines of code or so. I don't mind sharing, but do you know ColdFusion? Do you use ColdFusion for your server-side code?

Comment 105 by yanezricardo posted on 10/11/2012 at 4:26 PM

I do not know ColdFusion, I'm a .Net developer, web inexperienced, and I'm going to develop web applications for mobile devices with PhoneGap.

I thought about doing a demo application to read my dropbox photos and displays them in a list.

Thank you very much, I'm going to read about ColdFusion.

Comment 106 by Raymond Camden posted on 10/11/2012 at 5:45 PM

Ah, so you know .Net, but not necessarily in terms of web apps? As much as I'd like to see you learn ColdFusion, you really don't need too. This should be trivial in _any_ language. It's literally just a directory listing into an array turned to JSON. Shouldn't be more than 5 minutes of work in .Net, PHP, etc.

Comment 107 by yanezricardo posted on 10/11/2012 at 6:41 PM

yeah, just read some ColdFusion and you're right when you say that is not what I need right now ...

I think I'll fix it by creating an XML file with the metadata of the images, put the file in my dropbox folder, read the file and download images one by one.

Thank you very much for the info.

Comment 108 by Raymond Camden posted on 10/11/2012 at 6:43 PM

Sure, XML would be fine too. I just fine XML much more of a pain to deal with in JS. If you are hand crafting a file, why not hand write a JSON file? :)

Comment 109 by yanezricardo posted on 10/12/2012 at 2:59 AM

You're right! thanks again...

Comment 110 by Muthukumar posted on 10/17/2012 at 5:21 PM

I downloaded the list of pdf files from my server using file download, and able to view in my device during online mode. But I want to view the file during offline and also need to synchronize the files. Is there a way to do file synchronisation from the server using file download

Comment 111 by Raymond Camden posted on 10/17/2012 at 6:23 PM

So, yes, this is definitely possible, but it is a bit non-trivial. Not terribly complex, just involved. Let's break it down.

First, your app can work in offline mode easily enough. There is an API to check network status. Use that and if offline, then you just display the files already downloaded.

The sync aspect is more interesting. So if you are online, you can get the list of file onlines and compare to those on the app's file system. THe files not there - simple - download. If they match, it gets more complex. If the files are something the user can edit, then you need to provide a way to prompt the user about which is more fresh. If not, you could just download, but that is wasteful. So you may waht your remote system to return datestamps of the files as well and compare them to the local copies. That isn't going to be perfect either as the user's device time system will probably be in a different TZ then the server.

To handle that, you may wish to use something like LocalStorage or WebSQL to associate metadata with the files that can be compared to values on the server.

Comment 112 by Mark Lane posted on 10/24/2012 at 5:53 PM

Raymond,

Great posting and comment thread.

Have you had any experience or luck with using PhoneGap's FileTransfer download in conjunction with dynamically delivered / served images.

I follow your code...

http://www.raymondcamden.co...

returns a list of image file names, and they're obviously appending to arrive at resources for FileTransfer to download such as:

http://www.raymondcamden.co...

That's all well and good, and I confirmed this sort of stuff worked for me back at the start of my project (basically I confirmed that PhoneGap download worked).

The problem for me is that danger.jpg ultimately is a static resource and served up to anyone who knows the URL.

Id rather not have my images, and documents available to constant and static locations, so I'm attempting to serve them up through a script.

The backend is PHP, but obviously it doesn't really matter what server side language is involved.

http://www.example.com/docu...

I've done this many times before, and browsers are fine receiving images from scripts as long as they also send out the appropriate content type header, file size, and buffer the output.

In my example url, 12 represents and ID of a document or image. There are a myriad of reasons why I want to do this, mostly from a security standpoint. My actual urls contain other arguments representing rotating hashes and client user identifiers, etc.

The problem is that PhoneGap's FileTransfer download seems to not work with these dynamically generated images (try your captcha for example). I can't seem to make them palatable for ft.download.

Have you successfully downloaded images, documents etc from a dynamic source?

Files are created in the simulator (iOS) and on the devices, but they're empty. Zero (0) bytes.

Comment 113 by Raymond Camden posted on 10/24/2012 at 7:48 PM

Interesting. So - not knowing PHP, but knowing CF, I know that if I were to generate a dynamic image I'd _also_ ensure I used the right headers. Is your PHP code sending the right headers back?

Comment 114 by Mark Lane posted on 10/26/2012 at 4:45 PM

It was related to the headers... as far as I can tell this was related to additional headers (session related headers) that PhoneGap didn't like. Stripping the script down the bare minimum and taking it out of the context of the larger application seems to have addressed my issue. A working combination in PHP is as follows:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With');
header( "Content-Type: image/png" ); // Or whatever is appropriate for the file
header('Content-Length: ' . filesize( $sExampleFile ) );
ob_clean();
flush();
readfile( $sExampleFile );
exit();

Comment 115 by Raymond Camden posted on 10/26/2012 at 4:48 PM

Interesting. I wonder what head it was? If you can ever figure out, let me know. In theory, session stuff shouldn't matter to a file request. Unless your PHP code was checking for a login and redirecting.

Comment 116 by Rahul Kumar posted on 1/24/2013 at 2:48 PM

When i am using data/data in the place of Android/data i am getting error
the error code is 12.
Please help me.

Comment 117 by Raymond Camden posted on 1/24/2013 at 5:30 PM

According to the source code, 12 = path exists error. So your code is trying to make a path that already exists.

Comment 118 by Tony Gee posted on 1/25/2013 at 2:47 PM

Could the same method be used for videos instead of images. If so what should I look out for?

Comment 119 by Raymond Camden posted on 1/25/2013 at 4:55 PM

It could be used for anything. All I'd watch out for is the size of what you download of course.

Comment 120 by Chuck posted on 2/5/2013 at 1:53 AM

I have download working with FileTransfer, but on some devices I start getting the following in my debug and the downloads fail...

02-04 12:13:31.686: E/FileTransfer(2228): java.net.SocketException: Socket is closed
02-04 12:13:31.686: E/FileTransfer(2228): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.checkOpen(OpenSSLSocketImpl.java:262)
02-04 12:13:31.686: E/FileTransfer(2228): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:273)
02-04 12:13:31.686: E/FileTransfer(2228): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:257)
02-04 12:13:31.686: E/FileTransfer(2228): at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:210)
02-04 12:13:31.686: E/FileTransfer(2228): at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:477)
02-04 12:13:31.686: E/FileTransfer(2228): at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:441)
02-04 12:13:31.686: E/FileTransfer(2228): at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282)
02-04 12:13:31.686: E/FileTransfer(2228): at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232)
02-04 12:13:31.686: E/FileTransfer(2228): at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
02-04 12:13:31.686: E/FileTransfer(2228): at libcore.net.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:164)
02-04 12:13:31.686: E/FileTransfer(2228): at org.apache.cordova.FileTran...(FileTransfer.java:463)
02-04 12:13:31.686: E/FileTransfer(2228): at org.apache.cordova.FileTransfer.execute(FileTransfer.java:87)
02-04 12:13:31.686: E/FileTransfer(2228): at org.apache.cordova.api.PluginManager$1.run(PluginManager.java:192)
02-04 12:13:31.686: E/FileTransfer(2228): at java.lang.Thread.run(Thread.java:856)

Have you seen this before? It is not consistent between all devices and I can't make it fail in the simulator, but a few devices do it about 70% of the time. I have the code set to only try to run one download at a time, but that doesn't seem to fix it entirely either.

Comment 121 by Raymond Camden posted on 2/5/2013 at 1:54 AM

I'm sorry - that doesn't look familiar. Could you try posting to the PhoneGap Google Group (and please post back here if you get a response!)

Comment 122 by Nehal Vyas posted on 3/19/2013 at 5:44 PM

Hi - I could not download the files on device ready but i can download on any other event. It gives me error "File could not be found". I am using PhoneGap 2.4.0. Please help.

Comment 123 by Raymond Camden posted on 3/19/2013 at 5:47 PM

The error implies that it can't find the file you are trying to download. You are saying it works if you use another event? To be clear, the deviceready event is the only one you *must* wait for.

Comment 124 by Rajesh Jai posted on 5/16/2013 at 2:24 PM

Hi, i ma new to Cordova, i got this error while runing in Android2.3.6. "Could not find class 'android.webkit.WebResourceResponse', referenced from method org.apache.cordova.CordovaWebViewClient.getWhitelistResponse" Any solution for this. thanks in advance...

Comment 125 by Rajesh Jai posted on 5/16/2013 at 2:27 PM

hi, i am again. Cordova working in Android 4.x.. but i dont know why.? if know any one about this. please explain. thanks!!!

Comment 126 by Greg Hudy posted on 5/17/2013 at 7:25 PM

I was able to use the code above for synchronous downloading that Sumit Verma (http://www.blogonria.com/) had posted, modified it a bit and got it working for iOS pastebin.com/cMJYAgu1

Comment 127 by Anneleen posted on 8/8/2013 at 4:26 AM

Hi Raymond, I tried your example, but it's not doing anything for me. I replaced com.camden.imagedownloaddemo with my own path, but no success. The images aren't visible and the Console Logs are even empty.

Comment 128 by Raymond Camden posted on 8/8/2013 at 4:02 PM

When console.log fails me - I go back to the old school sucky alerts. I'd suggest adding some in and seeing if you can trace down exactly where the error occurs. You can also try Weinre.

Comment 129 by Phillip Senn posted on 8/8/2013 at 6:04 PM

The debugger; statement is your friend.

Comment 130 by Raymond Camden posted on 8/8/2013 at 6:05 PM

That's not going to work - by itself - in PhoneGap.

Comment 131 by Anneleen posted on 8/8/2013 at 6:22 PM

Added alerts, but no success...

Comment 132 by Raymond Camden posted on 8/8/2013 at 6:24 PM

So you get *nothing*? Even if you put an alert as the first line of JS code? You mind sharing your JS via a Gist or Pastebin.

Comment 133 by Anneleen posted on 8/8/2013 at 6:53 PM

Oh god, never mind, I should stop coding so late at night. I forgot to replace the cordova version. It's working now! Thanks for your help and your tutorial!

Comment 134 by Raymond Camden posted on 8/8/2013 at 8:38 PM

It happens - good luck.

Comment 135 by Anneleen posted on 8/17/2013 at 6:54 PM

Okay, I'm back. Just wondering how I would go about downloading a folder with different types of files, like for examples images, JSON- & jsfiles. Any idea?

Comment 136 by Raymond Camden posted on 8/17/2013 at 7:01 PM

By default, your web server should NOT be responding to a / request with a directory index, so it wouldn't work out of the box. What I've done in the past is to build server side code to handle this. So I may ask for "latestimages" (as a parameter to my server side code), it hits a particular folder, gets a list of files, and returns it as a JSON-encoded array. Then your PG code needs to loop over them and grab each.

I've actually got an example of this for images - but I can't remember the blog entry title offhand. Maybe just search for phonegap.

Comment 137 by Anneleen posted on 8/17/2013 at 11:45 PM

Fixed it! Now just one question left, is there a way to delete files that aren't used in the app anymore?

Comment 138 by Raymond Camden posted on 8/18/2013 at 4:52 AM

FileSystem API, right?

Comment 139 by Anneleen posted on 8/18/2013 at 5:44 AM

Yes ^^

Comment 140 by Raymond Camden posted on 8/19/2013 at 12:37 AM

So - are you agreeing you should use it? :) Just to be clear - I was saying - use the FS API. It can remove stuff from the file system.

Comment 141 by jay posted on 8/27/2013 at 7:03 PM

hi,
am using phone gap to download lots of files and it keeps telling me some files failed to download with time out error, thats on ios. is there a way i can increase the timeout cus it seems to time out after 60 secs. or any fix i can do

Comment 142 by Raymond Camden posted on 8/27/2013 at 7:07 PM

So to be clear, you are getting random errors? Or is it normally on larger files?

Comment 143 by jay posted on 8/27/2013 at 7:19 PM

its same error on large files

Comment 144 by jay posted on 8/27/2013 at 7:24 PM

this is the issue on http://stackoverflow.com/qu...

am the developer working on it

Comment 145 by Greg Hudy posted on 8/27/2013 at 7:48 PM

I posted a comment on the SO link. I have a slightly different approach that does work for me.

Comment 146 by jack posted on 8/27/2013 at 7:49 PM

i also have this error, not sure if its ios or phone gap, times out after 60sec, wish someone could tell me how to solve this!!!!!

FileTransferError {
body = "";
code = 3;
"http_status" = 0;

Comment 147 by jay posted on 8/27/2013 at 8:10 PM

greg i read you comment, for me am getting a list of files from an ajax post then i loop through to get each image and call filetranfer.download, but some of the files time out same kind of error like jack has, i output the error and i get some files timed out with code 3 error http_status = 0. but if i use a lan network no a wifi it works nice only fails on ios and wifi connection always times out after 60 secs of starting the download.

Comment 148 by Greg Hudy posted on 8/27/2013 at 8:18 PM

Does it give you the error after 60 sec of a single filetransfer.download or after 60 sec of total downloading time?

Comment 149 by jay posted on 8/27/2013 at 8:28 PM

60 sec of total download, even if i use a single file and it takes more then 60 secs it times out,

Comment 150 by Greg posted on 8/27/2013 at 9:35 PM

Are you doing the downloading synchronously or async? I have it working synchronously.

Comment 151 by Anneleen posted on 9/9/2013 at 7:00 PM

Is there a way to let the system download the files everytime there's internet connection?

Comment 152 by Raymond Camden posted on 9/9/2013 at 7:27 PM

@Anneleen: In theory, it is just as simple as removing the logic that only gets "new" files. Is that what you meant?

Comment 153 by Anneleen posted on 9/9/2013 at 7:33 PM

Oh, thanks for the tip, I finally got it working! ^^

Comment 154 by Ujjwal Das posted on 10/16/2013 at 9:47 AM

I am getting the following error while downloading image

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Comment 155 by Raymond Camden posted on 10/17/2013 at 1:40 AM

Are you talking to a server using HTTPS?

Comment 156 by Ujjwal Das posted on 10/17/2013 at 10:32 AM

Yes...I am making an ajax request to a self signed (https) server which got some certification error...Whenever I try to download from some other http/https server then its allow same with the similar code...

Since I am new to phone gap and eclipse, I am not able to handle such kind of exception..

My logcat is loaded with more error message but just providing you an selected Error message :

Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
xnet.provider.jsse.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:664)

I have used your above code for reference but still made no luck :(

Comment 157 by Raymond Camden posted on 10/17/2013 at 3:17 PM

There is an option to trust all hosts (self signed certs included). See the docs: http://cordova.apache.org/d...

Comment 158 by dmen posted on 10/25/2013 at 7:48 PM

I saw someone mention they were getting error 12 on getDirectory - and the error states the folder already exists. I'm getting this same error...
This isn't making sense to me - isn't getDirectory supposed to return the folder - even when the create flag is on? So that if the folder exists it's not created otherwise it is... If getDirectory() returns an error how to get the directory?
Also, looking in File explorer, I don't see the folder I am trying to create. I am using:
fileSystem.root.getDirectory("Android/data/com.gmrmarketing.healthy.re...", {create:true}, gotDir, gotError);

Comment 159 by dmen posted on 10/26/2013 at 12:12 AM

Little more on this - if I set create:true then I get error 12. If I set create:false I get error 1. I'm not sure what error 1 is, but have been at this for a while and would really like to be able to get a directory object.

Comment 160 by Raymond Camden posted on 10/26/2013 at 5:01 PM

Try the second option attribute, exclusive, and set it to false.

http://cordova.apache.org/d...

Comment 161 by dmen posted on 10/26/2013 at 7:12 PM

Thanks much for replying. Adding the flag doesn't seem to matter - with create:false I just get error: 1 as before and 12 with create true. This is quite frustrating, as I've got an approaching deadline and this seeming simple thing is taking way too long. Is there any device restrictions for this? The phone I'm testing on is an older LG Optimus V with Android 2.2.2 on it..

Comment 162 by Ujjwal Das posted on 10/28/2013 at 8:47 AM

Finally I resolved my SSL certificate error by inserting a few line of code which actually work for me...

public class MainActivity extends DroidGap {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setIntegerProperty("loadUrlTimeoutValue", 15000);
super.setStringProperty("loadingDialog", "loading your app...");
super.loadUrl("file:///android_asset/www/index.html");
//Call the fuction
trustEveryone();
}

public void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager(){
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
}

Comment 163 by dmen posted on 10/28/2013 at 2:44 PM

Doesn't seem to be a Android version issue. I tried the same code on one of the emulators at manymo.com and got the same error:1

Is there anywhere that describes how to get a folder within your installed application folder?

Comment 164 by Raymond Camden posted on 10/28/2013 at 2:54 PM

dmen - best I can suggest for now is - use what you are supposed to (create true and exclusive false) and if you still get 12, log a bug report. It should be working and if you can create a reproduceable case, you can then file a bug report.

Comment 165 by Raymond Camden posted on 10/28/2013 at 3:00 PM

Btw - something else to consider. Your path is this:

"Android/data/com.gmrmarketing.healthy.re..."

First - I think you need a / in front, don't you?
Second... and actually I change my mind - I don't think this is an issue - but I'll mention it anyway. You can't create N level deep stuff. Ie, "foo/moo" where both foo and moo don't exist. Last time I checked, if you needed to make 2 levels of folders deep, you had to make foo, then moo.

But I don't think that one is your issue.

Comment 166 by dmen posted on 10/28/2013 at 7:19 PM

Hi Raymond, thanks so much for helping me on this. I've managed to finally get it working, though I'm still not entirely sure why.

I moved the call to window.requestFileSystem into the deviceReady callback and it's working now. Originally, I had that call within another function that returned some xml. Not sure why it preferes to be in deviceReady but it's working - and I am using create:true, exclusive:false

Thanks!

Comment 167 by Raymond Camden posted on 10/28/2013 at 7:56 PM

"I moved the call to window.requestFileSystem into the deviceReady callback and it's working now. "

Woah... dude... seriously.... you know *all* PG stuff should be done after deviceReady, right? I assumed it was so well known that I never would have imagined you doing so without it.

Comment 168 by dmen posted on 10/28/2013 at 9:24 PM

I didn't mean to say it wasn't after deviceReady - it was. I got deviceReady and then I looked for an update xml file... if that file exists I then went ahead and do the requestFileSystem. That's what I was doing... which still seems it would work.

Comment 169 by mayank posted on 12/1/2013 at 1:37 AM

this is my code
<script type="text/javascript">
function onBodyLoad(){
document.addEventListener("deviceready", onDeviceReady, false);
}
function onDeviceReady() {
alert("device is ready");
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
downloadImage('http://upload.wikimedia.org...
}

function fail() {
alert("failed to get filesystem");
}

function gotFS(fileSystem) {
alert("got filesystem");

// save the file system for later access
alert(fileSystem.root.fullPath);
window.rootFS = fileSystem.root;
}

function downloadImage(url, fileName){
var ft = new FileTransfer();
alert(window.rootFS.fullPath + "/DCIM/Camera/" + fileName);
ft.download(
url,
// window.rootFS.fullPath + "/DCIM/Camera/" + fileName,
"/sdcard/DCIM/Camera/" + fileName,
function(entry) {
alert("download complete: " + entry.fullPath);

},
function(error) {
alert("download error" + error.code);
}
);
}

</script>

this code shows the message downloaded and file paht, but i can't find file in phone storage or sd card.

can you help me on this?

Comment 170 by Raymond Camden posted on 12/1/2013 at 6:48 PM

Not sure what to say. If you get a successful file down load and even see the path, then it should be there.

Comment 171 by mayank sood posted on 12/1/2013 at 7:02 PM

Hi,

The issue was , file was not showing in the folder, while the app was running and phone was attached with USB, when i reconnected with usb file appears in the folder..

one more question

for android i am using like

ft.download(
url, // mp3 path live
"/sdcard/DCIM/Camera/" + fileName, // phone storage folder path

will same path work for IOS? i am running on android and its working.. not sure about iphone for the path.

Thanks,

Mayank.

Comment 172 by Raymond Camden posted on 12/2/2013 at 12:40 AM

I can never remember paths for iOS. Dumb question - but did you try it?

Comment 173 by sabarish posted on 12/20/2013 at 3:41 PM

hi,
i need to do download a file and save it on the local memory i have used your code but it calls only init method.
please help how to fix this

Comment 174 by Raymond Camden posted on 12/20/2013 at 5:49 PM

Sabarish, if you are the same person who emailed me, I'll reply there.

Comment 175 by Dina Salah posted on 7/10/2014 at 1:19 PM

Hello,
I got error in this line :
$.get("http://www.raymondcamden.co...", {}, function(res) {

error msg: undefined $

I removed it then I got undefined "get"
who can I solve that?

Comment 176 by Raymond Camden posted on 7/10/2014 at 2:32 PM

If $ is undefined it means jQuery didn't load. Double check your script tag for it.

Comment 177 by dina salah posted on 7/12/2014 at 12:03 AM

yes I fixed it, but I got this error :
FileTransfer is not defined
this error in this line: var ft = new FileTransfer();
Do you have any idea?

Comment 178 by Raymond Camden posted on 7/12/2014 at 12:55 AM

You need to add the FileTransfer plugin.

Comment 179 by dina salah posted on 7/13/2014 at 2:01 AM

Yes, you are right, but the Error I got now:

07-12 23:56:40.180: E/(398): unsupported multiclient unsolicited response code 1009
07-12 23:56:41.870: E/FileTransfer(13259): {"target":"\/\/Android\/data\/com.camden.imagedownloaddemo\/cfguy.png","http_status":200,"code":1,"source":"http:\/\/images.wisegeek.com\/young-calico-cat.jpg","exception":"\/Android\/data\/com.camden.imagedownloaddemo\/cfguy.png: open failed: ENOENT (No such file or directory)"}

Comment 180 by Raymond Camden posted on 7/13/2014 at 7:36 PM

Can you share what line this error is thrown on?

Comment 181 by dina salah posted on 7/13/2014 at 8:30 PM

AI think this is the line:
ft.download("http://www.raymondcamden.co..." + escape(res[i]), dlPath, function(){

in the target directory

Comment 182 by Raymond Camden posted on 7/14/2014 at 3:08 AM

Not sure. The error message above mentions a different URL. Um, I'd have to dig into your app myself to help more. Sorry, I can't do that right now.

Comment 183 by Akashdeep Sharma posted on 7/15/2014 at 12:20 PM

Hii, i tried your code but it's only calling the init method and not the other. Please help.

Comment 184 by Scott J. Pearson posted on 7/15/2014 at 5:26 PM

Is there any way to download files via a POST request? I.e., specify the URL and certain parameters to pick the file, and then go down the usual download pathway. Thanks,

Comment 185 by Raymond Camden posted on 7/15/2014 at 7:26 PM

@Akashdeep: If onDeviceReady is not firing, ensure you are using cordova.js (or phonegap.js) in your HTML. Also - do remote debugging to see if you have some other error.

Comment 186 by Raymond Camden posted on 7/15/2014 at 7:29 PM

@Scott: From what I see of the docs it does NOT look possible. In theory you could do a POST with XHR and save the bits using the FIleSystem API.

Comment 187 by Scott J. Pearson posted on 7/15/2014 at 7:32 PM

The problem is that they are too large to store in memory (50 MB) as an AJAX call would require. I was hoping to try this to see if this provided a buffered approach.

Comment 188 by Raymond Camden posted on 7/15/2014 at 7:36 PM

I just checked the code (well the Java code) and it is definitely set to GET w/ no way around it. Best I can suggest is filing an issue for it.

Comment 189 by Akashdeep Sharma posted on 7/16/2014 at 12:34 PM

Hii i was calling the script twice that was creating the error. It's running now bt the image are not getting replace by the new one.. any idea on that. I tried your URL it download the images but didnt replace them plus they are not getting shown in the img src

Comment 190 by Raymond Camden posted on 7/16/2014 at 7:11 PM

@Akashdeep: I'd check with remote debugging to see if you can figure it out. I really can't tell from here.

Comment 191 by Akashdeep Sharma posted on 7/17/2014 at 9:06 AM

@raymond thank you for responding to the query. Just one last doubt. When i am downloading the pic i get the ERROR like {"code":1, "Source": source url; "target": target url, http status :null, body:null}. I want to know if you have used any other extra plugins. Because the folder are getting created but the file not getting replace. And do we need file in folder before we download. Please help in this. Thank you i advance. If you time we can talk via skype that would be a real help full

Comment 192 by Rolando Valcárcel posted on 7/18/2014 at 12:40 AM

Hi Raymond, nice tutorial , it's been really helpfull for me, but I'm having the same problem that Akashdeep.

Comment 193 by Rolando Valcárcel posted on 7/18/2014 at 2:47 AM

@Akashdeep I've fixed it with a couple of changes to the code here's a Pastebin of the code:
http://pastebin.com/hc6XpHgz , hope it helps you!

Comment 194 by Akashdeep Sharma posted on 7/18/2014 at 5:36 PM

@Rolando i was able solve the problem in an other way. I googled a lot and found that the code written by the Raymond is for rooted device and for the device that are not rooted can't used the path define in the code. I will soon paste the code wid some edits. Thank you @rolando :D

Comment 195 by Raymond Camden posted on 7/20/2014 at 6:56 AM

Glad you guys got it - was out of town.

Comment 196 by Irshad kk posted on 8/20/2014 at 10:02 AM

Hi Ray..
Where i can find the code for above to download.
Thanks

Comment 197 by Raymond Camden posted on 8/20/2014 at 3:04 PM

There isn't a separate download. All of the important code is in the blog entry.

Comment 198 by cyrielo posted on 9/10/2014 at 12:29 PM

Hello is this thread still active! i need help with my file download script it is throwing a java FileNotFound exception the code is -> http://pastebin.com/fPZTB2i2

Comment 199 by Chris Nalla posted on 10/24/2014 at 6:12 PM

Hello Ray,

Is it possible to do a file transfer it via USB? Nonetwork or any other form. Just connecting the phone (iOS/Android) and copy files from PC -> Mobile and Mobile -> PC

Thanks.

Comment 200 by Raymond Camden posted on 10/25/2014 at 1:29 AM

Hmm - I don't know honestly. I'd imagine there are some pretty strict security concerns there - you don't want a random app just getting full access to your file system.

Comment 201 by Samaludheen P.S posted on 8/31/2016 at 11:49 AM

Hi Raymond,

I recieve the image as Binary String. How can I store this in sqlite, the images are about 5 mb size.

Comment 202 (In reply to #201) by Raymond Camden posted on 8/31/2016 at 1:01 PM

Don't. You can, if you convert to Base64, but use the file system for images.