Using the Progress event in PhoneGap file transfers

This post is more than 2 years old.

Earlier today I was happy to hear that PhoneGap 2.7 was released. While perusing the changelist, I thought I read that progress events for file transfers were added in this release. However, I was wrong. FileTransfer has supported a progress event for a few months now. But since I figured this out while halfway through a demo, I figured I'd finish it up anyway and share it on my blog.

As you can imagine, the onprogress event is a property of the FileTransfer object. It is passed a progressEvent that is - unfortunately - not documented as far as I can see. The code example though gives you enough information I think to deal with it:

So from what I can see - you get a property that determines if the total size is known, and if so, you can get a percentage by using it with the loaded property. Otherwise you're just guessing at the time left, but at least you know something is going on.

For my demo, I thought I'd build a simple MP3 downloader/player. I googled for 'free open source music' and came across this incredible MP3 by Kansas Joe McCoy and Memphis Minnie: When the Levee Breaks. Go ahead and listen to it. As much as I love indie music and trance, the sound of old recordings like this is like pure gold to the ears.

Anyway - I began by creating an incredibly simple web page. It lists the name of the artists and the song along with a picture. I've included a button that will trigger the download. Also make note of the status div I'll be using for - you guessed it - progress events.

Here's a screen shot of it in action.

Ok, now let's take a look at the code.

Starting from the top, the first thing that may interest you is the file system request. I ask for the temporary file system so I have a place to store the mp3. Once I've got a hook to the file system I then enable the button in my web page and start listening for a touch event.

The function that handles the download, startDl, creates the FileTransfer object and points it to the remote MP3. I've used almost the exact same onprogress event as demonstrated in the PhoneGap docs. I changed it to write out a percentage when possible and in other cases, simply append dots to the end of a string. That way people know something is still being transferred.

The final portion simply handles the media portion. I didn't bother adding any real controls so it just begins to play the track and that's it. (To be clear, this wouldn't be hard to add, just check the Media API for more information.)

Watch the video below to see it in action:

As a last tip, note that the docs for Media seem to imply that you need a URI, not a file path. On iOS it seems to require a path, not a URI. Thanks to Simon MacDonald for helping me with this.

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

Archived Comments

Comment 1 by Peter Carabeo-Nieva posted on 5/2/2013 at 3:36 AM

I have a PhoneGap+jQM-based app where I hooked the progress event data to a nice Boostrap CSS-only animated progressbar (since jQM doesn't have progressbar UI) for providing status of uploads to an Amazon S3 bucket. The whole thing works like a charm and looks great, good stuff! :-)

I have to say that my only pain point with PhoneGap/Cordova so far is this long standing issue of camera.getPicture() not being able to support multiple media select at once: The only workarounds I know of are the hassle of integrating plugins to accommodate that or the one I did for myself which was to roll my own image gallery for multiple media select through use File API to read the images available on the device, create thumbnails of them via javascript-based image scaling algorithms, and implement caching/pagination for the whole thing.

Comment 2 by Cutter posted on 5/2/2013 at 1:34 PM

My favorite cover of that will always be Led Zepplin. I'm gonna have to check out that site.

Cool code Ray.

Comment 3 by Raymond Camden posted on 5/2/2013 at 3:53 PM

@Peter: Does the Capture API work for you instead?

Comment 4 by Peter Carabeo-Nieva posted on 5/2/2013 at 10:59 PM

@Raymond Nope, Capture API does allow multiple images/videos to be taken live on the device at once (limited by the optional "limit" parameter) but what I need is the ability to choose multiple existing media files (which had already been taken by the device's camera or copied over to the device's memory in the past) from the device's native gallery.

The current state of the PhoneGap Camera.getPicture method is akin to the web 1.0 days when an html form looking to accept some file from the user would throw a file dialog that only allows one to be selected. Then Flash's equivalent came along and we were able to hold down shift or control and select multiple files at once. I find it strange that cordova/phonegap has reached such maturity and yet still won't have this basic "multiple media select from native gallery" feature until some version past 3.0 or so :-/

Comment 5 by Peter Carabeo-Nieva posted on 5/2/2013 at 11:07 PM

Also, I chose the "code my own media gallery in the webview (with help from PG File API)" workaround instead of the "use a phonegap plugin" workaround because: 1) It didn't break my ability to still cloud compile through Build whereas using some random plugin would've 2) It allows me to serve the exact same multiple media select UI/functionality to all users instead of their being being differences between users based on their differing native galleries

Let me also mention that on Android I can use PG File API to access the entire file system but on iOS apps are sandboxed so I actually can't access the media files that are in the camera roll for instance :-/ (So this is where I really do need that multiple media select being supported by camera.getPicture)

Comment 6 by Raymond Camden posted on 5/3/2013 at 6:00 PM

Ah, Peter, thanks for clarifying that!

Comment 7 by Tony Awad posted on 5/11/2013 at 5:29 PM

Hello, great article. I really hope you do one about uploading to an amazon s3 bucket using the phonegap file transfer. Best wishes

Comment 8 by Raymond Camden posted on 5/11/2013 at 6:04 PM

I don't have one planned, but I know it has been done. I'd do a quick check of the Google group (ie load it and search it ;).

Comment 9 by Guillaume Bartolini posted on 5/28/2013 at 11:40 AM

Hi Raymond,

What is the best way to download multiple files from a server (json files) and have a progress event for the lot.

what i a have at the moment is a loop with my function inside...

Comment 10 by Raymond Camden posted on 5/28/2013 at 5:18 PM

Hmm. Let's forget about the progress for a bit. The best way (imo) to handle N async events is to use Deferreds in jQuery. They make it (somewhat) easy to say, "I've fired N things and when they are all done, do X."

The progress meter would be more difficult. You could do N progress meters of course. Windows does that - if I remember right, when copying/moving a set of files. One PM would be fairly difficult. You would need to adjust the Max value as you get more and more data in. To support that, you would need to associate on PM event with one file. I'm not sure you can do that. No... I think you could. Yeah, so you would need to create an aggregate file size.

I think it is possible. Just non-trivial. ;)

Comment 11 by Guillaume Bartolini posted on 5/29/2013 at 12:15 AM

Thanks for your reply Raymond. Sadly i am using zepto and i'm not sure there is a deferred function...

Comment 12 by Guillaume Bartolini posted on 5/29/2013 at 12:17 AM

Back again... Underscore might be my solution

Comment 13 by Shailendra posted on 6/19/2013 at 8:38 AM

I am getting the loaded size twice as that of the total size

fileTransfer.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
perc = Math.floor((progressEvent.loaded / * 100);
//perc = Math.floor(progressEvent.loaded / * 100);
status.text(perc + "% loaded...");
fileSize.text(" of "" bytes");
}// else {

Comment 14 by Raymond Camden posted on 6/19/2013 at 3:41 PM

It could possibly be this - from the docs: "On both Android an iOS, lengthComputable is false for downloads that use gzip encoding."

Comment 15 by Shailendra posted on 6/20/2013 at 8:31 AM

thank you Raymond for reply,

I tested lengthComputable in funbook, it is true. I dont understand why
progressEvent.loaded goes only twice of, another thing is 'download complete message' is fired only once at the end, it means download event is fired only once not twice

Comment 16 by Raymond Camden posted on 6/20/2013 at 6:21 PM

Yeah you got me there man. I would have to go deep into your code and test to give you firm reasons.

Comment 17 by Shailendra posted on 6/28/2013 at 1:28 PM

sorry raymond i have been busy somewhere else, did you got the same problem, i still couldn't find the reason, your feedback might be very helpful and give me some hints.
Thanks for giving your time in my problem

Comment 18 by Raymond Camden posted on 6/28/2013 at 3:53 PM

No, I'm not seeing it myself.

Comment 19 by Alexey Gladey posted on 7/1/2013 at 3:03 PM

I have ran into the same problem...
The same code works different on iOS and Android.. iOS ok, but getting 200% on Android... tried on Phonegap 2.7.0 and 2.9.0.. btw, i'm using Phonegap Build service.

Comment 20 by Raymond Camden posted on 7/1/2013 at 7:44 PM

Are you using gzip? The docs say this will change the values.

Comment 21 by florin cosmin onciu posted on 8/5/2013 at 11:15 AM

I have the same problem with the progress percent. It goes up to 200% in Android. I didn't tested on iOS. I am using Cordova 3.0
I think I have to adjust the value .. but it's not the best solution.
Or it might be a bug somewhere ?

Comment 22 by Aaron posted on 8/6/2013 at 12:00 PM

@Peter, could you link me to the phonegap plugin that will enable multiple image selecting ?

Comment 23 by Andrew Scott posted on 9/4/2013 at 6:21 PM

There does appear to be an issue with the .loaded value on android. The returned a value is x2 greater than what's actually been received.

I came across this post after discovering the issue on my own implementation of progress on FileTransfer. It's much the same as you have above, with the exception that I wasn't testing the .lengthComputable.

I've tried with different file types and with gzip enabled & disabled on the server; none of which made any changes to the .loaded value.

Cordova version 3.0.8 with an AVD that's running 4.3; tested with both Arm and Atom CPUs.

As a work around until a proper fix is found I've installed the device plugin :

cordova plugin add https://git-wip-us.apache.o...

And then modified the calculation using :

var loaded = device.platform == "Android" ? (prog.loaded / 2) : prog.loaded;
var pcnt = Math.round( (loaded / * 100 );

If anyone has resolved this issue properly please let us know.

Comment 24 by Raymond Camden posted on 9/4/2013 at 6:24 PM

Thanks for sharing this, Andrew!

Comment 25 by souravmitra posted on 9/17/2013 at 10:47 AM

I have seen video ,here downloading and playing the file is not taking more than 5 seconds but Incase of my build everything working fine but taking more than 10 seconds.Is it depend on bandwidth or download speed?

Comment 26 by Alex posted on 9/17/2013 at 2:14 PM

Hi Rayman,

Nice blog, i've found the tutorials on really useful.

Do you have a working example of how to download multiple files and firing an event when all are completed (as you mentioned before in a previous comment) with jQuery.Deferred?

If not no worries just thought I would check as I am struggling to get my head round how this could be achieved.



Comment 27 by Raymond Camden posted on 9/19/2013 at 6:07 PM

Sorry, I don't.

Comment 28 by Alex posted on 9/20/2013 at 11:59 AM

Thanks anyway, I managed to get multiple files upload working which is great, I think it was courtesy of a Simon McDonald!

Basically just put the files to be downloaded in an array and popped the last element off when it was succesfully download and just recalled the same function. It was pretty straight forward when you think about it, thanks again Simon!

Comment 29 by Raymond Camden posted on 9/20/2013 at 3:50 PM

Ok, glad you got it. :)

Comment 30 by Tarhe Oweh posted on 10/13/2013 at 5:48 PM

Great article. I got it to work first time without stress on a dialog box on Jquery Mobile. I got a problem with this line that goes up to 200%;

var perc = Math.floor(progressEvent.loaded / * 100);

But since I am using the code to download updates from within my application, I change the 100 to 50 which halve the total percentage to 100% maximum.

Comment 31 by Andrew Sctt posted on 10/13/2013 at 6:10 PM

@Tarhe - Remember that it's only android that's suffering with the incorrect .loaded value. Add the device plugin and check for device.platform == "Android" before making the adjustment.

Comment 32 by tarhe oweh posted on 10/13/2013 at 6:49 PM

@Andrew the Apps is meant to run on Android only, at least for now.

Comment 33 by rc1 posted on 11/5/2013 at 1:03 PM

how to pause and cancel download?

Comment 34 by Raymond Camden posted on 11/5/2013 at 5:12 PM

Please read the docs:

There is an abort method - but no pause.

Comment 35 by Shailendra posted on 11/8/2013 at 2:54 PM

Hi Raymond,
Could you please suggest me,
I have a zipped folder (may include images, small audio and video files), now i need to download and extract it to client device.

Thank you.

Comment 36 by Raymond Camden posted on 11/8/2013 at 4:40 PM

Try zip.js - http://gildas-lormeau.githu.... I've blogged about it before. Not in context of PhoneGap, but for another demo.

Comment 37 by Darmie posted on 11/26/2013 at 5:12 PM

Hi Raymond. Thanks for the tutorial. I have tried your code but it doesn't seem to work. when i click the download button nothing happens. I am using phonegap Build (Phonegap 3.0), tested the app on Android 4.3 device.

Comment 38 by Darmie posted on 11/26/2013 at 5:30 PM

Here's what i did:

<span id="dlink"><img src="img_bookdl.png"></span>
<span id="status"></span>

document.addEventListener('deviceready', deviceready, false);
var server = "";
var sectionname;

var buttonDom;
var statusDom;
var fileSystem;
function deviceready() {
console.log('dv ready');

//step one is to request a file system
window.requestFileSystem(LocalFileSystem.TEMPORARY, 0,
function(fs) {
buttonDom = document.querySelector('#dlink');
statusDom = document.querySelector('#status');
buttonDom.addEventListener('touchend', dlink, false);
fileSystem = fs;
}, function(e) {
alert('failed to get fs');
function StartDownload()

var fileTransfer = new FileTransfer();
var uri = encodeURI(" great gatsby.pdf");
var filePath = fileSystem.root.fullPath+'/studentlife.pdf';
fileTransfer.onProgress = function (progressEvent)
if (progressEvent.lengthComputable)
var perc = Math.floor(progressEvent.loaded / * 100);
statusDom.innerHTML=perc + "% loaded...";
function(entry) {
statusDom.innerHTML="download complete: " + entry.fullPath;
function(error) {
statusDom.innerHTML="download error source " + error.source;
statusDom.innerHTML="download error target " +;
statusDom.innerHTML="upload error code" + error.code;

Comment 39 by Raymond Camden posted on 11/29/2013 at 1:56 AM

Are you sure you added Filesystem support for the plugin? That's new for 3.0 and PhoneGap Build.

Comment 40 by Isabella Cris posted on 12/8/2013 at 6:51 PM


I am new to Phonegap and android. I am stuck in a task where I have to upload multiple files to the server. This also include database insertion operation (Fetch form entry values and store in DB). I am using HTML tag for browsing file (Image .jpg). Eg.
<input type="file" id="upload_file_1" />

Kindly help us in this to upload files on to server.

Thanks in advance.

Comment 41 by Raymond Camden posted on 12/8/2013 at 8:25 PM

"Kindly help us in this to upload files on to server."
This can be handled by the FileTransfer code I talk about in this blog entry. Please read the documentation and let me know if you have a *specific* question.

Comment 42 by Neerav posted on 12/12/2013 at 12:50 PM

Can you tell me how to show the progress of all files being downloaded from the server...

Comment 43 by Raymond Camden posted on 12/12/2013 at 9:45 PM

@Neerav: Not sure what you mean. Given N transfers - you can create N divs and update them as I did in the example above. Or you could attempt to use one progress display and do the math to figure out the right %.

Comment 44 by Colin Mahoney posted on 1/9/2014 at 7:51 PM

The problem with progressEvent.loaded having twice the expected value seems to be caused by the definition of the class *SimpleTrackingInputStream* in

SimpleTrackingInputStream overrides the three read methods from InputStream, in each case calling the corresponding and incrementing a counter by the number of bytes read. The problem is that InputStream implements _read(byte[] buffer)_ by calling _read(byte[] bytes, int offset, int count)_ - the end result is that the counter is incremented twice. Commenting out the method _SimpleTrackingInputStream....(byte[] bytes, int offset, int count)_ in solved the problem for me.

Comment 45 by Raymond Camden posted on 1/9/2014 at 8:29 PM

@Colin - thank you for doing this research. Cordova is open source - could you submit a pull request with that fix?

Comment 46 by Colin Mahoney posted on 1/9/2014 at 9:50 PM

OK, will do!

Comment 47 by Colin Mahoney posted on 1/10/2014 at 2:00 PM

I've submitted a pull request for the changes:

Also added comment to issue on JIRA:

Hope I got this right - haven't done it before!

Comment 48 by Raymond Camden posted on 1/10/2014 at 5:20 PM

Thank you - as a PG user - I appreciate it. :)

Comment 49 by Zoruninho posted on 4/23/2014 at 5:27 PM

Thanks a lot for this article and for your whole website! I'm currently working as an intern and I have to create an app which can record audio files and send them on a server and your work was a huge help for me.

The audio quality is still pretty bad (even in MPEG4 96Kbps) but it's working ;)

Comment 50 by Daniel posted on 5/22/2014 at 9:53 PM

Thank you, thank you, thank you ;-)
Great post!

Comment 51 by Raj posted on 6/20/2014 at 5:42 PM

hi Ray,

I have used the code snippet given in the article.

I am using jquery mobile, phonegap

I am getting the Crap something went wrong... error ., downloadPath,
function(entry) {
statusDom.innerHTML = "";
var media = new Media(entry.fullPath, null, function(e) { alert(JSON.stringify(e));});;

function(error) {
alert('Crap something went wrong...');

I have checked the url of the auido file from the server. but still i am getting the error.

Could you please guide me on this, what went wrong here


Comment 52 by Raymond Camden posted on 6/20/2014 at 5:52 PM

Change the 'crap' message to actually output the error and share that.

Comment 53 by Raj posted on 6/20/2014 at 6:03 PM

i have given the code

alert("download error source " + error.source);
alert("download error target " +;
alert("upload error code" + error.code);

it is giving source as my server filelocation
it is giving target file path
upload error code as 1

Comment 54 by Raj posted on 6/20/2014 at 6:39 PM

Hi Randy

A small observation , when i alert fileSystem.root.fullPath, it is only giving "/".

will that cause any issue? becuase as it is trying to download the mp3 to root directory

Please suggest me , i struck ed here

Comment 55 by Raymond Camden posted on 6/20/2014 at 6:48 PM

1 in FileTransfer errors means file not found. Ensure your source is right.

Comment 56 by Raj posted on 6/20/2014 at 6:54 PM

Surely the source is avalable, do we have any documentation on phonegap about the error codes

Is it related to destination ?

Comment 57 by Raymond Camden posted on 6/20/2014 at 6:57 PM

You can find the error codes here:

Comment 58 by Raj posted on 6/20/2014 at 7:00 PM

thank you Ray

Is there any thing that i am missing here ?

In android i am i am getting / when i use - fileSystem.root.fullPath

is this some thign that i need to look at ?

Comment 59 by Raymond Camden posted on 6/20/2014 at 7:21 PM

I think the issue is with the source, not the destination. You said it exists, are you 100% sure it does? Care to share the URL here? Also, maybe edit the access block of config.xml to ensure it is allowed.

Comment 60 by Raj posted on 6/20/2014 at 7:29 PM

Yes it is

I am able to fix it

it is because of the fullpath.

after changing fileSystem.root.fullpath to fileSystem.root.toURL()

it is working.

I need to check the functionality completely.

I will confirm you once it is working

thank you soo much

Comment 61 by Raymond Camden posted on 6/20/2014 at 7:29 PM

Ok cool. FileSystem stuff has changed a *lot* lately.

Comment 62 by Raj posted on 6/20/2014 at 7:31 PM

and one more observation is , if change from temparary to persistant, it is now working

any suggestions ?

Comment 63 by Raymond Camden posted on 6/20/2014 at 7:31 PM

You got it working though, right?

Comment 64 by Raj posted on 6/20/2014 at 7:34 PM

Yes with LocalFileSystem.TEMPORARY but not with LocalFileSystem.PERSISTENT

Comment 65 by Raymond Camden posted on 6/20/2014 at 7:39 PM

I'd look at the FileSystem plugin docs. I know some folders are read only and some are meant for your data.

Comment 66 by Raj posted on 6/20/2014 at 7:43 PM

sure thank you Ray

When i alert target path , it is saying localhost as one of the directory in the path, but it should show my application name than localhost.

I have seen that you have answered some where else, i lost that thread.

if you remember, can you share me that

Comment 67 by Raymond Camden posted on 6/20/2014 at 7:44 PM

What I'm saying is - look at the docs for the FileSystem. They have aliases now for common paths, and some are *specific* for you to save stuff too.

Comment 68 by Raj posted on 6/20/2014 at 7:58 PM

Sure thank you.

Comment 69 by Sanafan posted on 8/28/2014 at 6:21 PM

Hey Raymond and thanks first for this great tutorial.

I'm just working on a file upload (video) to a server and this works fine. Even your progress event works fine and tells me the percentage.

But how can i realise a small green bar under my video, that displays the actual state of the progress? The Text is nice, but he doesn't look good. I would like to have a small bar, which gets more green the more the upload is done.

Can you tell me, how this could be realised?

Thanks in Advance!

KR from Germany!

Comment 70 by Raymond Camden posted on 8/30/2014 at 4:37 PM

One simple way would be to use a green gif sized appropriately - so 10% of max for 10% completed, and just update the width of the graphic.

Comment 71 by Mohammed posted on 9/24/2014 at 4:55 PM

You are awesome.
you need to change fullPath to toURL() in you code.

Comment 72 by Raymond Camden posted on 9/24/2014 at 6:03 PM

Yep, the file API has updated quite a bit.

Comment 73 by Danilo Cicognani posted on 10/14/2014 at 12:18 PM

Thank you Raymond for sharing your knowledge.
I often finish to read one of your posts when I'm trying to understand something about the Cordova platform or one of his plugins.

I'm in pain with this problem: I'm starting multiple downloads in background, saving the files with temporary names (to avoid saving incomplete or corrupted files if connection is lost): after each download finished (in success callback) I rename the temporary file with the correct name.

This works very well in Android, but in iOS I found that the success callback is never called.

It's seams to be a bug in org.apache.cordova.file-transfer plugin (I found various issues about it:,, This means that I can only save files with temporary names: by now I set a workaround to use temporary names only on Android, but obviously I'm not satisfied.

I'm using Cordova version 3.6.3-0.2.13 and org.apache.codova.file-transfer plugin version

I found that the on_progress callback is always called, also in iOS, so I tried to use it to understand if the file download was completed.
When (progressEvent.loaded ==, the download is completed, perfect, but how can I know which one specific file download is completed?
There is not "entry" parameter in on_progress callback or in progressEvent parameter.
Any idea?

Thank you and sorry for my English.

Comment 74 by Raymond Camden posted on 10/14/2014 at 2:57 PM

That's a real good question. I don't know. I'll do some digging.

Comment 75 by Raymond Camden posted on 10/14/2014 at 2:59 PM

So to be clear, the success handler is not firing for iOS due to the bugs you mentioned?

Comment 76 by Danilo Cicognani posted on 10/14/2014 at 6:10 PM

The success handler, but also the error handler, are never fired in iOS.
Only onprogress is fired.

Comment 77 by Raymond Camden posted on 10/14/2014 at 8:29 PM

You got me then. Unless you can wait for the iOS bug to be fixed?

Comment 78 by Danilo Cicognani posted on 10/15/2014 at 12:01 PM

Sorry, I didn't understand.
I don't know why the success or error handler are never fired in iOS (but it's all ok in Android): I think is because of the bug I pointed out, but I'm not sure.
By now I finished to use temporary file names only for Android and not for iOS, but I was trying to find a workaround, to have the App behave in the same way in the two platforms.
If I don't find it, I will wait for a new version of the plugin, hoping it will fix my problem...

Comment 79 by Farzad YZ posted on 6/16/2016 at 8:01 AM

Great Article! one thing to note here is that in current version of cordova 6, the fullPath method is deprecated and instead you could use toURL method which holds the file:/// protocol schema inside it. Another weird thing is that Android ignores onprogress event.

Comment 80 (In reply to #79) by Raymond Camden posted on 6/16/2016 at 3:58 PM

In theory you could just use XHR2 now. :) That should support progress events fine.

Comment 81 (In reply to #80) by Farzad YZ posted on 6/16/2016 at 4:02 PM

Actually after playing around with the file transfer finally I implemented the unprogressive event. Looks like the problem was it could not determine the total volume to be downloaded and I solved it by getting the volume from the server manually.

Comment 82 by Greg Hudy posted on 10/25/2016 at 1:53 PM

Hi Ray, any idea why the progress isn't firing on GZipped files?

Comment 83 (In reply to #82) by Raymond Camden posted on 10/25/2016 at 2:11 PM

Sorry no. I'd file a bug report on the plugin. Also - it may be a web server thing. I believe it has to report the size to the requestor, and it may not be doing so for gzip files.

Comment 84 (In reply to #83) by Greg Hudy posted on 10/25/2016 at 2:57 PM

Thanks for the info. I'll try to look into it a bit.

Comment 85 by Shairyar Baig posted on 11/15/2016 at 12:17 PM

Thanks for sharing it, helped me a lot.