Twitter: raymondcamden


Address: Lafayette, LA, USA

Downloading files to a PhoneGap application - Part 1

01-19-2012 54,488 views Mobile 200 Comments

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:

view plain print about
1<!DOCTYPE HTML>
2<html>
3<head>
4<meta name="viewport" content="width=320; user-scalable=no" />
5<meta http-equiv="Content-type" content="text/html; charset=utf-8">
6<title>Image Download Demo</title>
7
8<script type="text/javascript" charset="utf-8" src="phonegap-1.3.0.js"></script>
9<script type="text/javascript" charset="utf-8">
10//Global instance of DirectoryEntry for our data
11var DATADIR;
12
13//Loaded my file system, now let's get a directory entry for where I'll store my crap    
14function onFSSuccess(fileSystem) {
15    fileSystem.root.getDirectory("Android/data/com.camden.imagedownloaddemo",{create:true},gotDir,onError);
16}
17
18//The directory entry callback
19function gotDir(d){
20    DATADIR = d;
21    var reader = DATADIR.createReader();
22    reader.readEntries(gotFiles,onError);
23}
24
25//Result of reading my directory
26function gotFiles(entries) {
27    console.log("The dir has "+entries.length+" entries.");
28 for (var i=0; i<entries.length; i++) {
29 console.log(entries[i].name+' '+entries[i].isDirectory);
30 }
31}
32
33function onError(e){
34    console.log("ERROR");
35    console.log(JSON.stringify(e));
36}
37
38function onDeviceReady() {
39    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, null);    
40}
41
42function init() {
43document.addEventListener("deviceready", onDeviceReady, true);
44}
45</script>
46
47</head>
48<body onload="init();" >
49<h2>Image Download Demo</h2>
50
51<div id="status"></div>
52
53</body>
54</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:

view plain print about
1<!DOCTYPE HTML>
2<html>
3<head>
4<meta name="viewport" content="width=320; user-scalable=no" />
5<meta http-equiv="Content-type" content="text/html; charset=utf-8">
6<title>Image Download Demo</title>
7<script type="text/javascript" charset="utf-8" src="jquery.min.js"></script>
8<script type="text/javascript" charset="utf-8" src="phonegap-1.3.0.js"></script>
9<script type="text/javascript" charset="utf-8">
10//Global instance of DirectoryEntry for our data
11var DATADIR;
12var knownfiles = [];    
13
14//Loaded my file system, now let's get a directory entry for where I'll store my crap    
15function onFSSuccess(fileSystem) {
16    fileSystem.root.getDirectory("Android/data/com.camden.imagedownloaddemo",{create:true},gotDir,onError);
17}
18
19//The directory entry callback
20function gotDir(d){
21    console.log("got dir");
22    DATADIR = d;
23    var reader = DATADIR.createReader();
24    reader.readEntries(function(d){
25        gotFiles(d);
26        appReady();
27    },onError);
28}
29
30//Result of reading my directory
31function gotFiles(entries) {
32    console.log("The dir has "+entries.length+" entries.");
33 for (var i=0; i<entries.length; i++) {
34 console.log(entries[i].name+' dir? '+entries[i].isDirectory);
35        knownfiles.push(entries[i].name);
36        renderPicture(entries[i].fullPath);
37 }
38}
39
40function renderPicture(path){
41    $("#photos").append("<img src='file://"+path+"'>");
42    console.log("<img src='file://"+path+"'>");
43}
44
45function onError(e){
46    console.log("ERROR");
47    console.log(JSON.stringify(e));
48}
49
50function onDeviceReady() {
51    //what do we have in cache already?
52    $("#status").html("Checking your local cache....");    
53    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, null);    
54}
55
56function appReady(){
57    $("#status").html("Ready to check remote files...");
58    $.get("http://www.raymondcamden.com/demos/2012/jan/17/imagelister.cfc?method=listimages", {}, function(res) {
59        if (res.length >
0) {
60            $("#status").html("Going to sync some images...");
61            for (var i = 0; i < res.length; i++) {
62                if (knownfiles.indexOf(res[i]) == -1) {
63                    console.log("need to download " + res[i]);
64                    var ft = new FileTransfer();
65                    var dlPath = DATADIR.fullPath + "/" + res[i];
66                    console.log("downloading crap to " + dlPath);
67                    ft.download("http://www.raymondcamden.com/demos/2012/jan/17/" + escape(res[i]), dlPath, function(){
68                        renderPicture(dlPath);
69                        console.log("Successful download");
70                    }, onError);
71                }
72            }
73        }
74        $("#status").html("");
75    }, "json");
76
77}
78
79function init() {
80document.addEventListener("deviceready", onDeviceReady, true);
81}
82</script>
83<style>
84img {
85    max-width: 200px;
86}
87</style>
88</head>
89<body onload="init();" >
90<h2>Image Download Demo</h2>
91
92<div id="status"></div>
93
94<div id="photos"></div>
95
96</body>
97</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:

view plain print about
1ft.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:

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

Related Blog Entries

200 Comments

These comments will soon be imported into Disqus. To add a comment, use Disqus above.
  • Commented on 01-19-2012 at 11:20 PM
    I Thought the cache.manifest was for storing offline files like images. This is all completely new info for me. Thanks for the code!
  • Commented on 01-20-2012 at 5:28 AM
    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.
  • Commented on 01-20-2012 at 8:07 PM
    Thanks for this post. I need it.

    M.
  • Commented on 01-20-2012 at 11:39 PM
    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);
       });
    }
  • Commented on 01-21-2012 at 8:28 AM
    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).
  • Commented on 01-22-2012 at 2:16 AM
    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
  • Commented on 01-22-2012 at 9:25 AM
    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.
  • Commented on 01-22-2012 at 10:10 PM
    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.
  • Commented on 01-23-2012 at 5:45 AM
    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.
  • Commented on 01-23-2012 at 8:31 AM
    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/group/phonegap/browse_thr...
  • Commented on 01-24-2012 at 9:51 AM
    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.
  • Commented on 01-24-2012 at 9:54 AM
    Thanks Simon.
  • Commented on 01-24-2012 at 10:02 AM
    Thanks for testing this Simon (didn't get a chance to yet). Please post back when you hear more.
  • Commented on 01-24-2012 at 10:28 AM
    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.
  • Commented on 01-24-2012 at 10:41 AM
    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!
  • Commented on 01-24-2012 at 10:49 AM
    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?
  • Commented on 01-24-2012 at 12: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.org/repos/asf?p=incubato...

    or the regularly updated mirror on github:

    https://github.com/apache/incubator-cordova-androi...

    You won't have long to wait for the official 1.4 though as it should be out on Monday.
  • Alex #
    Commented on 02-26-2012 at 8:35 AM
    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
  • Commented on 02-26-2012 at 9:48 AM
    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.
  • Alex #
    Commented on 02-27-2012 at 1:12 PM
    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.
  • Commented on 02-27-2012 at 1:17 PM
    If you just want to store that date, why bother using a file? Just use localStorage.
  • Alex #
    Commented on 02-29-2012 at 11:27 AM
    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
  • Commented on 02-29-2012 at 11:30 AM
    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.
  • Alex #
    Commented on 03-01-2012 at 3:32 AM
    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.
  • Commented on 03-01-2012 at 5:53 AM
    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.
  • Alex #
    Commented on 03-01-2012 at 6:27 AM
    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 :-)
  • Commented on 03-01-2012 at 6:32 AM
    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. :)
  • Alex #
    Commented on 03-01-2012 at 7:19 AM
    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.
  • Commented on 03-01-2012 at 8:53 AM
    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.
  • Alex #
    Commented on 03-01-2012 at 9:45 AM
    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.
  • Commented on 03-01-2012 at 9:54 AM
    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();
       }
    }
  • Commented on 03-01-2012 at 10:12 AM
    Sumit, please please please use pasteBin in the future. :)
  • Commented on 03-01-2012 at 11:55 AM
    Sure, sorry :)
  • Commented on 03-01-2012 at 11:58 AM
    You can delete the earlier comment, if you want. Here is the paste bin version:

    http://pastebin.com/kNvF166F
  • Commented on 03-01-2012 at 12: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. ;)
  • Commented on 03-01-2012 at 12: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 ;))...
  • Alex #
    Commented on 03-01-2012 at 12: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 :-)
  • Commented on 03-01-2012 at 12: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).
  • Commented on 03-01-2012 at 12: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. ;)
  • Alex #
    Commented on 03-05-2012 at 8:22 AM
    @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.
  • Commented on 03-05-2012 at 10:21 AM
    Alex,

    Data is a struct returned from CF.

    data.imagePath = "http://domain.com/path/to/image/folder";
    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
  • Commented on 03-05-2012 at 10:24 AM
    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.
  • Alex #
    Commented on 03-05-2012 at 11:17 AM
    Sumit,

    so data.imageList is almost the same as Raymonds
    $.get("http://www.raymondcamden.com/demos/2012/jan/17/ima...;, {}, 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 ;-)
  • Commented on 03-05-2012 at 11:32 AM
    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.
  • Alex #
    Commented on 03-05-2012 at 12: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?
  • Alex #
    Commented on 03-05-2012 at 2:08 PM
    Oh my god, that was really stupid...

    i used console.Log instead of console.log....
  • Commented on 03-05-2012 at 11:01 PM
    Alex - I do that a lot myself. Don't feel bad. :)
  • Shailesh #
    Commented on 03-07-2012 at 6:01 AM
    Hi, http://www.raymondcamden.com/demos/2012/jan/17/ima...;
    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.
  • Commented on 03-07-2012 at 7:17 AM
    That is a file on my ColdFusion server. It could be any technology - PHP, Ruby, whatever. For me, I used ColdFusion.
  • Jerry Zhu #
    Commented on 03-20-2012 at 10:54 AM
    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.FileTransfer.download(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?
  • Commented on 03-20-2012 at 3:05 PM
    What are you downloading? Can you share all the code? (Via pastebin of course.)
  • Jerry Zhu #
    Commented on 03-20-2012 at 9:45 PM
    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..
  • Commented on 03-21-2012 at 5:34 AM
    So to be clear, it dies on the actual file download? You see all the other (previous) status messages as the app starts up?
  • Jerry Zhu #
    Commented on 03-21-2012 at 9:28 AM
    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..
  • Commented on 03-21-2012 at 7:45 PM
    What I'd recommend then is adding some console.log messages. You need to find out exactly where it breaks.
  • Jerry Zhu #
    Commented on 03-23-2012 at 10:09 AM
    Could I send email to you with screenshots? I'm using phonegap 1.5.
  • Commented on 03-23-2012 at 10:39 AM
    Screenshots won't help. Try what I recommended - using console.log to try to diagnose where exactly it's failing.
  • Shailesh #
    Commented on 03-30-2012 at 5:07 AM
    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?
  • Shailesh #
    Commented on 03-30-2012 at 5:33 AM
    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....
  • Commented on 03-30-2012 at 6:26 AM
    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.
  • Commented on 04-11-2012 at 11:30 AM
    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?
  • Commented on 04-11-2012 at 1: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.
  • Commented on 04-11-2012 at 5:38 PM
    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.
  • Commented on 04-13-2012 at 4:54 PM
    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.
  • Shailesh #
    Commented on 05-04-2012 at 5:39 AM
    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.
  • Commented on 05-04-2012 at 9:09 AM
    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.
  • Commented on 05-16-2012 at 3:47 PM
    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}, ...)
  • Commented on 05-16-2012 at 3:49 PM
    Forgot to mention that I run it on Android 2.3.7
  • Commented on 05-16-2012 at 3:55 PM
    @Gavriel

    Yes, please see my earlier comment.

    http://www.raymondcamden.com/index.cfm/2012/1/19/D...

    Sumit
  • Commented on 05-16-2012 at 5:03 PM
    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
  • Commented on 05-16-2012 at 6:18 PM
    adding to config.xml:
    <feature name="http://api.phonegap.com/1.0/file"/>;
    <access origin="https://mydomain.com"; />
    fixed it
  • Commented on 05-31-2012 at 6:03 AM
    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.
  • Commented on 05-31-2012 at 8:19 AM
    When does this error get thrown?
  • Commented on 05-31-2012 at 10:52 AM
    When i am running my application it thrown this error.
  • Commented on 05-31-2012 at 10:54 AM
    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.
  • Commented on 05-31-2012 at 11:03 AM
    I got this error from console log.
  • Commented on 05-31-2012 at 11:06 AM
    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.
  • Commented on 06-07-2012 at 6:06 AM
    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));

    }
  • Commented on 06-07-2012 at 8:31 AM
    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.
  • Commented on 06-08-2012 at 4:49 AM
    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.
  • Commented on 06-09-2012 at 7:30 AM
    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.
  • Commented on 06-09-2012 at 8:25 AM
    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/en/tutorials/file/filesy...
  • Kenji #
    Commented on 06-25-2012 at 9:14 PM
    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
  • Commented on 06-26-2012 at 8:32 AM
    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.
  • Commented on 07-03-2012 at 3:47 AM
    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.com/demos/2012/jan/17/&qu...; + escape(res[i]), dlPath, function(){....

    what should I do?
  • Commented on 07-03-2012 at 12: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.
  • Commented on 07-04-2012 at 2: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
  • Commented on 07-04-2012 at 8:33 AM
    That's a new one for me. I'd suggest the PhoneGap Google group. Sorry.
  • AMet #
    Commented on 07-18-2012 at 4:18 AM
    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.
  • Commented on 07-18-2012 at 10:35 AM
    Thanks AMet.
  • Commented on 07-18-2012 at 11:54 AM
    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/jira/browse/CB-995.
  • Jane #
    Commented on 07-25-2012 at 8:04 PM
    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.
  • Commented on 07-26-2012 at 9:43 AM
    I'm having a hard time parsing your comment. You say it works on a real device but not the emulator?
  • Jane #
    Commented on 07-26-2012 at 6:27 PM
    Yes
    code dosen't work on emulator.
    The getDirectory method keeps throwing error.
    error code is 12
  • Julius #
    Commented on 07-27-2012 at 1:32 AM
    Hello,

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

    Thanks !!
  • Julius #
    Commented on 07-27-2012 at 1: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
  • Commented on 07-27-2012 at 5:57 AM
    Ah so it is all good now?
  • Abraham #
    Commented on 08-02-2012 at 6:26 PM
    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?
  • Commented on 08-03-2012 at 9:29 AM
    Best I can recommend is using the console to try to track down where things are failing.
  • Abraham #
    Commented on 08-03-2012 at 4:21 PM
    got it working, I read about the console a few minutes later of post, tnx!
  • yanezricardo #
    Commented on 10-10-2012 at 6:58 PM
    Hello, could you tell me who is
    "imagelister.cfc?method=listimages"? at URL: ("http://www.raymondcamden.com/demos/2012/jan/17/ima...;)

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

    Thanks in advance!
  • Commented on 10-10-2012 at 8:14 PM
    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.
  • yanezricardo #
    Commented on 10-11-2012 at 7:05 AM
    Could you please give me a snippet? or tell me how to look for information about creating that file and please excuse the audacity!
  • Commented on 10-11-2012 at 7:13 AM
    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?
  • yanezricardo #
    Commented on 10-11-2012 at 7:26 AM
    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.
  • Commented on 10-11-2012 at 8:45 AM
    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.
  • yanezricardo #
    Commented on 10-11-2012 at 9:41 AM
    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.
  • Commented on 10-11-2012 at 9:43 AM
    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? :)
  • yanezricardo #
    Commented on 10-11-2012 at 5:59 PM
    You're right! thanks again...
  • Muthukumar #
    Commented on 10-17-2012 at 8:21 AM
    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
  • Commented on 10-17-2012 at 9:23 AM
    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.
  • Commented on 10-24-2012 at 8:53 AM
    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.com/demos/2012/jan/17/ima...

    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.com/demos/2012/jan/17/dan...

    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/documentsLibrary/12

    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.
  • Commented on 10-24-2012 at 10:48 AM
    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?
  • Commented on 10-26-2012 at 7:45 AM
    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();
  • Commented on 10-26-2012 at 7:48 AM
    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.
  • Rahul Kumar #
    Commented on 01-24-2013 at 3:48 AM
    When i am using data/data in the place of Android/data i am getting error
    the error code is 12.
    Please help me.
  • Commented on 01-24-2013 at 6:30 AM
    According to the source code, 12 = path exists error. So your code is trying to make a path that already exists.
  • Tony Gee #
    Commented on 01-25-2013 at 3:47 AM
    Could the same method be used for videos instead of images. If so what should I look out for?
  • Commented on 01-25-2013 at 5:55 AM
    It could be used for anything. All I'd watch out for is the size of what you download of course.
  • Commented on 02-04-2013 at 2:53 PM
    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.FileTransfer.download(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.
  • Commented on 02-04-2013 at 2:54 PM
    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!)
  • Nehal Vyas #
    Commented on 03-19-2013 at 8:44 AM
    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.
  • Commented on 03-19-2013 at 8:47 AM
    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.
  • Rajesh Jai #
    Commented on 05-16-2013 at 5:24 AM
    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...
  • Rajesh Jai #
    Commented on 05-16-2013 at 5:27 AM
    hi, i am again. Cordova working in Android 4.x.. but i dont know why.? if know any one about this. please explain. thanks!!!
  • Commented on 05-17-2013 at 10:25 AM
    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
  • Commented on 08-07-2013 at 7:26 PM
    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.
  • Commented on 08-08-2013 at 7:02 AM
    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.
  • Commented on 08-08-2013 at 9:04 AM
    The debugger; statement is your friend.
  • Commented on 08-08-2013 at 9:05 AM
    That's not going to work - by itself - in PhoneGap.
  • Commented on 08-08-2013 at 9:22 AM
    Added alerts, but no success...
  • Commented on 08-08-2013 at 9:24 AM
    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.
  • Commented on 08-08-2013 at 9:53 AM
    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!
  • Commented on 08-08-2013 at 11:38 AM
    It happens - good luck.
  • Commented on 08-17-2013 at 9:54 AM
    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?
  • Commented on 08-17-2013 at 10:01 AM
    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.
  • Commented on 08-17-2013 at 2:45 PM
    Fixed it! Now just one question left, is there a way to delete files that aren't used in the app anymore?
  • Commented on 08-17-2013 at 7:52 PM
    FileSystem API, right?
  • Commented on 08-17-2013 at 8:44 PM
    Yes ^^
  • Commented on 08-18-2013 at 3:37 PM
    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.
  • jay #
    Commented on 08-27-2013 at 10:03 AM
    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
  • Commented on 08-27-2013 at 10:07 AM
    So to be clear, you are getting random errors? Or is it normally on larger files?
  • jay #
    Commented on 08-27-2013 at 10:19 AM
    its same error on large files
  • jay #
    Commented on 08-27-2013 at 10:24 AM
    this is the issue on http://stackoverflow.com/questions/18386271/ios-ph...

    am the developer working on it
  • Greg Hudy #
    Commented on 08-27-2013 at 10:48 AM
    I posted a comment on the SO link. I have a slightly different approach that does work for me.
  • jack #
    Commented on 08-27-2013 at 10:49 AM
    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;
  • jay #
    Commented on 08-27-2013 at 11:10 AM
    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.
  • Greg Hudy #
    Commented on 08-27-2013 at 11:18 AM
    Does it give you the error after 60 sec of a single filetransfer.download or after 60 sec of total downloading time?
  • jay #
    Commented on 08-27-2013 at 11:28 AM
    60 sec of total download, even if i use a single file and it takes more then 60 secs it times out,
  • Greg #
    Commented on 08-27-2013 at 12:35 PM
    Are you doing the downloading synchronously or async? I have it working synchronously.
  • Commented on 09-09-2013 at 10:00 AM
    Is there a way to let the system download the files everytime there's internet connection?
  • Commented on 09-09-2013 at 10:27 AM
    @Anneleen: In theory, it is just as simple as removing the logic that only gets "new" files. Is that what you meant?
  • Commented on 09-09-2013 at 10:33 AM
    Oh, thanks for the tip, I finally got it working! ^^
  • Ujjwal Das #
    Commented on 10-16-2013 at 12: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.
  • Commented on 10-16-2013 at 4:40 PM
    Are you talking to a server using HTTPS?
  • Ujjwal Das #
    Commented on 10-17-2013 at 1: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 :(
  • Commented on 10-17-2013 at 6:17 AM
    There is an option to trust all hosts (self signed certs included). See the docs: http://cordova.apache.org/docs/en/3.1.0/cordova_fi...
  • dmen #
    Commented on 10-25-2013 at 10:48 AM
    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.recipes/images", {create:true}, gotDir, gotError);
  • dmen #
    Commented on 10-25-2013 at 3:12 PM
    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.
  • Commented on 10-26-2013 at 8:01 AM
    Try the second option attribute, exclusive, and set it to false.

    http://cordova.apache.org/docs/en/3.1.0/cordova_fi...
  • dmen #
    Commented on 10-26-2013 at 10:12 AM
    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..
  • Ujjwal Das #
    Commented on 10-27-2013 at 11:47 PM
    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();
       }
    }
    }
  • dmen #
    Commented on 10-28-2013 at 5:44 AM
    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?
  • Commented on 10-28-2013 at 5:54 AM
    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.
  • Commented on 10-28-2013 at 6:00 AM
    Btw - something else to consider. Your path is this:

    "Android/data/com.gmrmarketing.healthy.recipes/images"

    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.
  • dmen #
    Commented on 10-28-2013 at 10:19 AM
    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!
  • Commented on 10-28-2013 at 10:56 AM
    "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.
  • dmen #
    Commented on 10-28-2013 at 12: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.
  • Commented on 11-30-2013 at 2:37 PM
    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/wikipedia/commons/thum...','350px-Wiktionary_small.svg.png');
    }

    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?
  • Commented on 12-01-2013 at 7:48 AM
    Not sure what to say. If you get a successful file down load and even see the path, then it should be there.
  • Commented on 12-01-2013 at 8:02 AM
    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.
  • Commented on 12-01-2013 at 1:40 PM
    I can never remember paths for iOS. Dumb question - but did you try it?
  • sabarish #
    Commented on 12-20-2013 at 4:41 AM
    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
  • Commented on 12-20-2013 at 6:49 AM
    Sabarish, if you are the same person who emailed me, I'll reply there.
  • Dina Salah #
    Commented on 07-10-2014 at 4:19 AM
    Hello,
    I got error in this line :
    $.get("http://www.raymondcamden.com/demos/2012/jan/17/ima...;, {}, function(res) {

    error msg: undefined $

    I removed it then I got undefined "get"
    who can I solve that?
  • Commented on 07-10-2014 at 5:32 AM
    If $ is undefined it means jQuery didn't load. Double check your script tag for it.
  • dina salah #
    Commented on 07-11-2014 at 3:03 PM
    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?
  • Commented on 07-11-2014 at 3:55 PM
    You need to add the FileTransfer plugin.
  • dina salah #
    Commented on 07-12-2014 at 5:01 PM
    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)"}
  • Commented on 07-13-2014 at 10:36 AM
    Can you share what line this error is thrown on?
  • dina salah #
    Commented on 07-13-2014 at 11:30 AM
    AI think this is the line:
    ft.download("http://www.raymondcamden.com/demos/2012/jan/17/&qu...; + escape(res[i]), dlPath, function(){


    in the target directory
  • Commented on 07-13-2014 at 6:08 PM
    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.
  • Akashdeep Sharma #
    Commented on 07-15-2014 at 3:20 AM
    Hii, i tried your code but it's only calling the init method and not the other. Please help.
  • Commented on 07-15-2014 at 8:26 AM
    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,
  • Commented on 07-15-2014 at 10:26 AM
    @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.
  • Commented on 07-15-2014 at 10:29 AM
    @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.
  • Commented on 07-15-2014 at 10:32 AM
    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.
  • Commented on 07-15-2014 at 10:36 AM
    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.
  • Akashdeep Sharma #
    Commented on 07-16-2014 at 3:34 AM
    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
  • Commented on 07-16-2014 at 10:11 AM
    @Akashdeep: I'd check with remote debugging to see if you can figure it out. I really can't tell from here.
  • Akashdeep Sharma #
    Commented on 07-17-2014 at 12: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
  • Rolando Valcárcel #
    Commented on 07-17-2014 at 3:40 PM
    Hi Raymond, nice tutorial , it's been really helpfull for me, but I'm having the same problem that Akashdeep.
  • Rolando Valcárcel #
    Commented on 07-17-2014 at 5:47 PM
    @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!
  • Akashdeep Sharma #
    Commented on 07-18-2014 at 8:36 AM
    @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
  • Commented on 07-19-2014 at 9:56 PM
    Glad you guys got it - was out of town.
  • Commented on 08-20-2014 at 1:02 AM
    Hi Ray..
    Where i can find the code for above to download.
    Thanks
  • Commented on 08-20-2014 at 6:04 AM
    There isn't a separate download. All of the important code is in the blog entry.
  • cyrielo #
    Commented on 09-10-2014 at 3:29 AM
    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
  • Chris Nalla #
    Commented on 10-24-2014 at 9:12 AM
    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.
  • Commented on 10-24-2014 at 4:29 PM
    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.