Be sure to read the notes at the end of this blog entry!
This week I had the opportunity to record a few videos on PhoneGap for Adobe TV. One of the videos covered the File API and since it was a bit difficult I figured I'd share my findings, and sample code, with others.
My main struggle with the File API was trying to wrap my head around how it worked. The docs weren't entirely clear to me and were a bit confusing. Turns out there's a good reason for that. (Although I'm working to improve the docs.) PhoneGap's File API is really an implementation of the W3 File API. The PhoneGap docs mention something similar in the database area so it makes sense for the File docs to be updated as well. (And as I said - I'm working on that. I did my first pull request to add just such a mention.)
After I figured that out, I then found an incredibly useful article on the File API over at HTML5 Rocks: Exploring the Filesystem APIs. I encourage everyone to read over Eric Bidelman's article. He's got examples for pretty much every part of the API.
At a high level, working with the File API comes down to a few basic concepts:
- First, you request a file system. You can ask for either a persistent or temporary file system. On the desktop, these both point to a sandboxed folder. On PhoneGap, your access is a bit broader, essentially the entire storage system.
- The API supports basic "CRUD" operations for both files and folders.
- The API supports reading and writing to files, both binary and plain text.
- Probably the most difficult aspect (well, not difficult, just a bit unwieldy), is that each and every operation is asynchronous. So to get and read a file involves about 3 or 4 levels of callbacks.
For my Adobe TV video, I built a simple application that demonstrates some of these principles. I began with a few simple buttons that would let me test basic file operations:
In order to do anything, I need access to the file system, and this needs to be done after PhoneGap fires the deviceready event:
//request the persistent file system
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, onError); } function init() {
document.addEventListener("deviceready", onDeviceReady, true);
}
function onDeviceReady() {
If the file system is loaded, onFSSuccess will handle storing a pointer to it while also setting up my event handlers:
function onFSSuccess(fs) {
fileSystem = fs; getById("#dirListingButton").addEventListener("touchstart",doDirectoryListing);
getById("#addFileButton").addEventListener("touchstart",doAppendFile);
getById("#readFileButton").addEventListener("touchstart",doReadFile);
getById("#metadataFileButton").addEventListener("touchstart",doMetadataFile);
getById("#deleteFileButton").addEventListener("touchstart",doDeleteFile); logit( "Got the file system: "+fileSystem.name +"<br/>" +
"root entry name is "+fileSystem.root.name + "<p/>") doDirectoryListing();
}
As a quick aside, getById is simply a wrapper for document.getElementById. (Trying to reduce my dependency on jQuery.) Our fileSystem object has a few properties we can display, like the name for example. It also has a root property which is a pointer to the root directory. (Duh.) The logit function is simply appending to a DIV on the HTML page as a quick debugging technique.
This event handler then fires off doDirectoryListing. This is normally run by the "Show Directory Contents" button but I automatically run it after the file system is opened.
}
s+="<p/>";
logit(s);
} function doDirectoryListing(e) {
//get a directory reader from our FS
var dirReader = fileSystem.root.createReader(); dirReader.readEntries(gotFiles,onError);
}
function gotFiles(entries) {
var s = "";
for(var i=0,len=entries.length; i<len; i++) {
//entry objects include: isFile, isDirectory, name, fullPath
s+= entries[i].fullPath;
if (entries[i].isFile) {
s += " [F]";
}
else {
s += " [D]";
}
s += "<br/>";
Reading bottom to top, the event handler starts off by creating a reader object off the root property of the file system object. To get the files, you simple call readEntries, and use a callback to handle the result. The entries (which can be files or directories) are a simple array of objects. Here's an example of the output:
So what about file reading and writing? Opening a file is simple. You can simply run getFile(name) and the API can (if you want) also create the file if it doesn't exist. This simplifies things a bit. Here's the event handler and call back for clicking "Creating/Append to Test File".
f.createWriter(function(writerOb) {
writerOb.onwrite=function() {
logit("Done writing to file.<p/>");
}
//go to the end of the file...
writerOb.seek(writerOb.length);
writerOb.write("Test at "+new Date().toString() + "\n");
}) } function doAppendFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, appendFile, onError);
}
function appendFile(f) {
Again - please read up from bottom to top. You can see the use of getFile here along with the options after it to ensure an error won't be thrown if it doesn't exist. Appending to a file is done by creating a writer object. Do note - and I screwed this up myself - if you don't seek to the end of the file you'll actually overwrite data as opposed to appending. Now let's look at reading:
function doReadFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, readFile, onError);
}
function readFile(f) {
reader = new FileReader();
reader.onloadend = function(e) {
console.log("go to end");
logit("<pre>" + e.target.result + "</pre><p/>");
}
reader.readAsText(f);
}
As before, we begin by opening the file, and in the success callback, create a FileReader object. You can read text or binary data depending on your needs. In this example our content is all text so we readAsText and in that callback append it to our div.
Now let's look at metadata. This method doesn't return a lot of data - just the modification date of the file/directory.
function doMetadataFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, function(f) {
f.getMetadata(metadataFile,onError);
}, onError);
}
function metadataFile(m) {
logit("File was last modified "+m.modificationTime+"<p/>");
}
Finally - let's look at the delete operation:
function doDeleteFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, function(f) {
f.remove(function() {
logit("File removed<p/>");
});
}, onError);
}
I hope these examples make sense. If it isn't obvious, I slightly tweaked my style as I built each of the sections. Sometimes I wrote the callbacks within the API calls and sometimes I did it separately. I've included the full code below as well as an APK for those of you who want to test on Android.
<!DOCTYPE HTML>
<html> <head>
<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Minimal AppLaud App</title> <script type="text/javascript" charset="utf-8" src="phonegap-1.4.1.js"></script>
<script type="text/javascript" charset="utf-8">
var fileSystem; //generic getById
function getById(id) {
return document.querySelector(id);
}
//generic content logger
function logit(s) {
getById("#content").innerHTML += s;
} //generic error handler
function onError(e) {
getById("#content").innerHTML = "<h2>Error</h2>"+e.toString();
} function doDeleteFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, function(f) {
f.remove(function() {
logit("File removed<p/>");
});
}, onError);
} function metadataFile(m) {
logit("File was last modified "+m.modificationTime+"<p/>");
} function doMetadataFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, function(f) {
f.getMetadata(metadataFile,onError);
}, onError);
} function readFile(f) {
reader = new FileReader();
reader.onloadend = function(e) {
console.log("go to end");
logit("<pre>" + e.target.result + "</pre><p/>");
}
reader.readAsText(f);
} function doReadFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, readFile, onError);
} function appendFile(f) { f.createWriter(function(writerOb) {
writerOb.onwrite=function() {
logit("Done writing to file.<p/>");
}
//go to the end of the file...
writerOb.seek(writerOb.length);
writerOb.write("Test at "+new Date().toString() + "\n");
}) } function doAppendFile(e) {
fileSystem.root.getFile("test.txt", {create:true}, appendFile, onError);
} function gotFiles(entries) {
var s = "";
for(var i=0,len=entries.length; i<len; i++) {
//entry objects include: isFile, isDirectory, name, fullPath
s+= entries[i].fullPath;
if (entries[i].isFile) {
s += " [F]";
}
else {
s += " [D]";
}
s += "<br/>"; }
s+="<p/>";
logit(s);
} function doDirectoryListing(e) {
//get a directory reader from our FS
var dirReader = fileSystem.root.createReader(); dirReader.readEntries(gotFiles,onError);
} function onFSSuccess(fs) {
fileSystem = fs; getById("#dirListingButton").addEventListener("touchstart",doDirectoryListing);
getById("#addFileButton").addEventListener("touchstart",doAppendFile);
getById("#readFileButton").addEventListener("touchstart",doReadFile);
getById("#metadataFileButton").addEventListener("touchstart",doMetadataFile);
getById("#deleteFileButton").addEventListener("touchstart",doDeleteFile); logit( "Got the file system: "+fileSystem.name +"<br/>" +
"root entry name is "+fileSystem.root.name + "<p/>") doDirectoryListing();
} function onDeviceReady() { //request the persistent file system
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, onError); } function init() {
document.addEventListener("deviceready", onDeviceReady, true);
} <style>
button { width: 100%; padding: 5px; }
</style>
</head> <body onload="init();" id="stage" class="theme"> <button id="addFileButton">Create/Append to Test File</button>
<button id="readFileButton">Read Test File</button>
<button id="metadataFileButton">Get Test File Metadata</button>
<button id="deleteFileButton">Delete Test File</button>
<button id="dirListingButton">Show Directory Contents</button> <div id="content"></div> </body>
</html>
</script>
Edited August 27, 2013: Just as an FYI, the File API has been updated a few times since I wrote this blog post. You will note the version link in the text above is for PhoneGap 1.5. PhoneGap is now version 3. One of the big places it changed was in the readAsText area. In my code, I call getFile from the file system object and then pass that into the reader object via the readAsText method. That does not work now. The object passed to getFile is a FileEntry object. You can think of it as a higher-level container for file data. That object has a file() method that returns the file. That thing can then be used in readAsText. You can see an example of this modification in this PhoneGap Google Group posting: Post
Archived Comments
this is really good..
I am looking for a way to read files syncronious. any way that I can do that with PhoneGap?
It is mainly my Read functions that need to wait for the read to complete..
I h+know that I need to watch out for these syncronios thing but in this case I believe i need it.
Thanks
Kim
That isn't part of the File API, so you're out of luck.
Hmm.. I kind a though that.. it is just a pain to have 3-4 call-back's to read a file.. ;-)
Thanks again for all your support
Kim
OK I am getting further .. but here is a senario:
I have a number of XML files in a folder that i need to list .. but using the xml data.. not the XML name..
I can get it going but then I have too obay the JQMobile syntax..
This is not wining.. (you guys are dooing fantastic stuff!!) just a challange for you and the the team ..
Thanks again for the support
Kim
Not sure what you mean by 'xml data' versus 'xml name'.
The name and description for the list is in the XML.. not the XML file it self.
So the challange is to keep all in sync when building he list.
Thanks
Kim
Yeah, I'm still not getting you. :) Maybe you can share an example of what you mean?
The idear is that I need to list name and description from a number of local XML files for further selection..
I have it working now, but I am actually at a point that the order never is the same, it is not important but it is just another ignoring thing..
My point here is the many number of Callbacks..not that I can not get it going..
Thanks for listising :-)
Kim
Thank you.
Your posting was really really helpful.
I spent too much time to find good example for PhoneGap File API.
In the article you said "The API supports reading and writing to files, both binary and plain text."
I didn't find a way to write binary data, could you make an example of doing that? I looked all over and every place I got some information said that its not possible.
There are different reading methods for binary data. Check the API here:
https://developer.mozilla.o...
Let me know if that doesn't help.
Hey Raymond thanks for your response, the problem is not read the file but write binary data to a file!
The main IDEIA that what I have to do is, create an image with HTML5 Canvas, than save this image to the device.
So according to your post .. "The API supports reading and writing to files, both binary and plain text." .. this is possible, but I didn't have success.
The only way I found to do this is sending the image to a SERVER and then using phonegap FileTransfer download it to the device, but to me this is not an option, the best option would be write binary to the file.
On some research I found something like this, "you can't write binary data from JavaScript in PhoneGap. It is
a limitation of passing data between the native and JavaScript layer as everything has to be a string."
So I think I will have to find another way.
Recent browsers include support for binary data in JS arrays.
But... let me dig into this. I just did a blog post on Watermarking Images in phonegap - so I could use that. No promised ETA - but please ping me if you don't hear back from me.
I wasn't able to get this today - and Monday is a holiday. But gonna try my best to get this Tuesday.
Thank you Raymond, I think the watermark sample is a nice way to do this, try to save it to the device file storage.
Thanks again for your response.
Hi Raymond,
Do you see it being possible to read audio files from the filesystem and play them with the HTML5 audio api? Maybe even getting metadata out of the files?
Keen to have a go at a simple audio player but I'm seeing some challenges...
Yep, it should be possible. As for the metadata, I've used this before:
http://blog.nihilogic.dk/20...
And it worked great. I was actually hoping to build a little demo with this during the week.
This is excellent, following the doco had me really lost
Hi Raymond.
Is possible copy one file "sqllite db" from the sd card to internal memory using phonegap api file? Please, you have an example?
It may be possible. I'd check this blog entry to see if it helps: http://www.raymondcamden.co...
is not it. I would like to send a file after installing the apk and not along with the apk
I believe it would be possible. The blog entry I linked too assumed a file shipped with the APK, but you could modify it to download the file using the File API. See the PhoneGap docs on using file downloads.
Thank you. I know it is quite complicated to copy a file from sd to internal memory.
until the moment not a practical example of this
Just wondering would this be what I need to allow for In-App purchases to be stored and called? My app is FREE and I want people to be able to buy and view magazines.
I've never done InApp purchases, but I'd imagine you are storing files + data to signify that a user has access to some add on. Therefore you would maybe use this + the SQL stuff.
Hi Raymond,
Your post is much clearer then the docs but have you tried this on phonegap version higher than 2.0? Since version 2.0 plugin.xml is intergrated into config.xml. I'm using phonegap 2.2.0 in eclipse and never get any file api start to work. If you have success in v2.2.0, could you send me the whole project file so that I know how to config.
Thanks,
Max
But this blog post isn't making use of any plugin.xml or config.xml file. I'm not quite sure what your comment means.
I think I may have run into some misunderstanding of using File API. Btw, would you please send me(halo_mx@hotmail.com) all files in the project for a reference.
Thanks,
Max
Did you try the download link?
Oh, isn't it a compiled apk file? I basicly want to see the project files so that I know how you config for the conversion(from page to app).
Oh sorry, right, but the full source code is right above it.
If device doesnot contains SD card..then where it will save the text file?
Basically - if you can't get a file system, then you can't write. So if there is no SD card and no internal storage, you will be stopped at that stage.
fine thanks:)
Great article, so I downloaded the APK and ran it in a tablet - it worked well.
I then produced an APK from the complete source and phonegap-1.4.1.js, using PhoneGapBuild (I renamed the sample.html as index.html to satisfy PhoneGapBuild, and included icon.png and config.xml in the zip that I uploaded to PhoneGapBuild).
But after this new APK is installed in the tablet, none of the buttons generate any output and there are no error messages.
Any ideas? Am I missing something? Can you confirm that the complete source is usable in PhoneGapBuild?
If you pushed up a zip to PhoneGap Build that included the physical phonegap-1.4.1.js, than that was a mistake. When you use PG Build, your HTML should refer to
<script src="phonegap.js"></script>
or
<script src="cordova.js"></script>
But you do NOT include the file. PG Build automatically puts this file in when making builds.
Hi Raymond,
I want to refer to more than half year old post (by Marlon) regarding writing binary file to local FileSystem. I'm dealing with exactly same problem where I download from server large chunks of binary data and I need to store it in file on Applications file system. Is there any solution available to do it?
Andrej
I have *not* checked this, but 2.4.0 mentions "binary data" support in the release notes. Unfortunately I can't tell exactly what they mean by this.
I can say that 'chunks' should work now - they added slice() support for file reads.
after couple of day waffling around the problem I realized that binary data support is actually available in cordova 2.4.0 but there are limitations. It is not a direct implementation of HTML5 FileSystem as it doesn't support Blob. In my case I could use FileTransfer to get binary data on my app file system. The problem was bringing binary data to webview. Android 4+ works better in this respect as ArrayBuffer can be used, but for anything older I had to convert base64 using charCodeAt which returns and integer. This looks incredibly inefficient so I'm working on some work around right now.
Thanks for posting this. Please keep me updated.
at last a tutorial explaining how to read and write files in phonegap. Thanks very much. I've been going nuts trying to work out whats going on but now I understand, shame its so tricky! thanks again
Excuse me, is the name of fileSystem.root always "sdcard"?
If I want to access the filesystem, is the best way to use "window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, onError);", using LocalFileSystem instead of sdcard?
I don't believe it is safe to assume sdcard. I think you should do it as you describe - via requestFileSystem.
Thanks Raymond, I found something weird, as I type:
adb shell
df
It shows me stroage/sdcard0.
But I have to use vindow.requestFileSystem(sdcard/~/~, 0, onFSSuccess, onError); to access the file.
Why don't they match?
And another question,
indow.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSSuccess, onError);
Where does LocalFileSystem.PERSISTENT point to?
Please take a look to this issue in stackoverflow which I posted.
http://stackoverflow.com/qu...
Thank you so much.
Is there a way to read a local file from the local directory of the application? For example, to load data from a csv file the first time the application loads? Is this possible to be done using PhoneGap's File API?
@Stallman: Q1, um I don't know. :) I just use the API and I recommend you do as well.
Q2: Where does it point to? You can output the directory in the onFSSuccess result to see yourself. It is part of the filesystem object. Assuming you have 'fs' as the argument to your success handler, I believe you want fs.root.fullPath.
Andreas, you can skip using the FileIO and just use Ajax directly. That would work great for a CSV file.
Hi, that was very halpful, but is there any way to overwrite the contents of the file? i have a usecase where i need to do that.. thank you :)
Check out FileWriter (http://docs.phonegap.com/en..., it will allow you to overwrite a file.
I am using this on iphone to store images, what is the maximum limit to the storage, I have read 5mb and have read it is limited only by the phones storeage capacity..
Hi..
I am trying to upgrade my App. From 2.5 to 2.7.. but suddenly my read file returns empty... I can list files but not read them..
Have you heard of any changes?
It happens on both iPhone and Android..
Thanks in advance
Kim
@Nisan: As far as I know it is the phone's storage capacity.
@Kim: I have not, but have you checked the docs?
Hmm..
Ok I do believe that I did read the manuals, but it was originally developed in 2.2 so it could be something alog the time that was changed and the backwards compatibility was dereciated in 2.7.. I guess I will release with 2.5 and the do some more research..
Thanks anyway for your always swift replies
Don't forget there is a dropdown in the upper right corner of the docs page. It lets you jump back and forth quickly.
Yep I have tried that and the toggel is not super easy but did not reveal anything between 2.5 and 2.7 .. (Have not moved back in versions) I plan to do a small test script and see if I can identify the problem.. Whats get me is that it is on iPhnoe and Android, and on local Compiled and on build.phonegap.com .. so it is something fundamental in PG.. Not a problem as long as I know what it is :-)
Please keep us informed of what you find!
Raymond, thanks. While I still don't have a *definitive* answer i do have an answer...lol
OK, after allog of testing and rewriting I believe I have identified the issue..
My original code looked like this:
-------------
function loadQuestFromFileORG(sGUID) {
var sFileName;
var sXml = '';
if (fileSystem && questDirEntry) {
//Load file from local storage
sFileName = sGUID + '.quest';
questDirEntry.getFile(sFileName, {create:false}, function(f) {
var reader = new FileReader();
reader.onloadend = function(evt) {
sXml = evt.target.result;
questCurrentQuest.loadQuestFromString(sXml);
loadStatus(sGUID);
};
reader.readAsText(f);
}, fsFail);
} else
log(2,"File System not available.. Quest not read..");
}
----------
I changed it to :
---
function loadQuestFromFile(sGUID) {
var sFileName;
if (fileSystem && questDirEntry) {
//Save the file to the local
sFileName = sGUID + '.quest';
questDirEntry.getFile(sFileName, {create:false}, gotQuestFileEntry);
} else
log(2,"File System not available.. Quest not read..");
}
function gotQuestFileEntry(fileEntry) {
fileEntry.file(gotQuestFile, fsFail);
}
function gotQuestFile(file){
readQuestFile(file);
}
function readQuestFile(file) {
var reader = new FileReader();
reader.onloadend = function(evt) {
var sXml = evt.target.result;
questCurrentQuest.loadQuestFromString(sXml);
log(5,"After load GUID in quest is: " + questCurrentQuest.guid);
if (questCurrentQuest.guid.length > 1) loadStatus(questCurrentQuest.guid);
};
reader.readAsText(file);
}
----
I build it to avid having 3 helper functions ... But I believe I was missing the Function between Entry and File.. I have not tested the old method with the extra layer.. But I believe I had hit and used an unexpected function that worked before but is not fixed..
Thanks
Kim
Hi,
I am also trying to write binary data with phonegap but I don't manage to get it working. I see many people having same issues. do you have any clues about how to solve this problem?
Thank you.
Can you share more about how you are trying to do it? I know the FileTransfer API can do it.
The code that I have is mGWT code. I use get-phonegap which is a javascript phonegap wrapper. The differents steps are the same as in JavaScript.
In my case, I make a query on a distant python server to read a file. After a query the server open the file, read its content and return it as a base64 encoded string in a Blob object.
Here is my code:
(It's full of alert popup because in the context it's the only way for me to debug)
public void onSuccess(Blob result) {
blob = result;
Window.alert("Distant file data has been read, start phonegap");
phoneGap.getFile().requestFileSystem(FileSystem.LocalFileSystem_PERSISTENT, 0, new FileCallback<FileSystem, FileError>() {
@Override
public void onSuccess(FileSystem entry) {
flags = new Flags();
flags.setCreate(true);
Window.alert("Got file system, start getdir");
entry.getRoot().getDirectory("MyFolder", flags, new FileCallback<DirectoryEntry, FileError>() {
@Override
public void onSuccess(DirectoryEntry entry) {
Window.alert("We got the dir, start getting file to write (" + entry.getFullPath() + ")");
entry.getFile(file.getFileName(), flags, new FileCallback<FileEntry, FileError>() {
@Override
public void onSuccess(FileEntry entry) {
Window.alert("We got the file to write, start writing (" + entry.getFullPath() + ")");
entry.createWriter(new FileCallback<FileWriter, FileError>() {
@Override
public void onSuccess(FileWriter entry) {
Window.alert("File created, start writing");
entry.setOnWriteEndCallback(new WriterCallback<FileWriter>() {
@Override
public void onCallback(FileWriter result) {
Window.alert("File has been sucessfully written.");
}
});
entry.setOnErrorCallback(new WriterCallback<FileWriter>() {
@Override
public void onCallback(FileWriter result) {
Window.alert("Failed to write the file :'(");
}
});
entry.write(b64decode(blob.toString()));
}
@Override
public void onFailure(FileError error) {
Window.alert("Failed to write the file : " + error.getErrorCode());
}
});
}
@Override
public void onFailure(FileError error) {
Window.alert("Failed to get the file : " + error.getErrorCode());
}
});
}
@Override
public void onFailure(FileError error) {
Window.alert("Failed to get directory : " + error.getErrorCode());
}
});
}
@Override
public void onFailure(FileError error) {
Window.alert("Failed to request file system : " + error.getErrorCode());
}
});
}
});
In the future, please use Pastebin or Gists for large code blocks please. I had to Google to see what mGWT is. That's a new one on me.
If you using Base64, you aren't using binary, you are using binary data encoded as a string. But why use Base64 at all. Why not make your server return the bits and make use of the FileTransfer API instead?
Oh, sorry. I still dont know how write blob data via FileWriter. I get zip file from server, unpack it and try to write blob entries.
Like this:
entry.getData(function(content) {
fileWriter.write(content); //content is Blob
});
But it not working.
Can you help me?
According to the docs for FileEntry (http://docs.phonegap.com/en... there is no getData function.
Sorry. I used oldest version of cordova. In 2.9 it works.
PS. entry - is entry from ZIP, not fileEntry.
Thanks.
So then to be clear - you got stuff working? If so - great. :)
Hey Raymon,
That is very good example. but if i want to read the content of a js file that is present in our www folder. then how can we read this because it tells always sdcard as a root directory. so how can i access that js file content.
Hi,,,Is it possible to create zip file using phonegap file api ?
Hmm. Technically no. It isn't part of the API. But - there is a JavaScript library for creating and reading zip files. You could use that.
@Manish: Why are you trying to _read_ a JS file? Normally you want to include it, a script tag would do just that.
@Raymond : yes i know the way to include the js file but my js file is encrypted and i have to decrypt it first and then include, that's why i am asking..
and $.getScript() method does not run the encrypted js file
Can I ask why you are encrypting it? It isn't secure at all. I can use any web inspector on your app, load the JS, decrypt it, and have the source. It sounds like security by obscurity and not really worth your time (imo).
yes, i am using it for security purpose actually phonegap apps apk files are easily extracted. so i need to encrypt my code. and want to load my decrypt js file at run time so it only present in RAM. can you help me to do this. that how can i access this encrypted js file. Thanks
But you are using JS code to decrypt it. The same JS code you can extract from the APK. So explain to me how this is secure? I extract your APK. I run it myself - I have your code - done.
yea i am using js code to decrypt it . but i am encrypt my file using jcryption and the key is generated in java file so without key anyone cannot find the src code
So your using a plugin then to call custom java code shipping with your PhoneGap app?
To be clear - even then you may not be safe. I can take your code, extract it, add code to send a copy of the JS to a server, and repackage it up.
I honestly think you are making a mistake here.
That being said - I would load the JS using jQuery's $.get and set the type to text.
this is not some kind of plugin
i am accessing a variable variable from java file that contains the key and at run time i decrypt the js file content using that key. i think no one can find out the js code becoz physically js file is in encrypted format.
and if you know about the security of js file then please tell me
please give me your gmail id for future help
If you mean JavaScript when you say Java, please say JavaScript. There are vastly different things. If you are using a key in a JS file to decrypt another JS file, then you are not secure. I can take that key and decrypt it myself.
Again - shipping an encrypted JS file that you decrypt in your code is NOT going to be secure imo.
you are misunderstood see i have a variable in MainActivity.java file and use that variable in js file to decrypt another js file content.
Ok, so I extract your code, take your main, unencrypted JS, add code in there after you decrypt the other JS file and have the contents of the file sent to me, or logged to the console. Package up the code and run it on my system.
you add you code but you cannot read my code without key
can we meet on chat. please give me your gmail id
But you are shipping a way to _get_ the key. You said you get the key on start up and want to use that to decrypt it. I'm saying I can modify your code so that after you've done that, the unencrypted code is going to be sent to me.
No - I will not meet on chat but you can email me via my contact form. As I said though I do not believe what you are doing is safe. If you think it is, then so be it.
ya i got it it will not safe.
do you know the way to secure my js files if yes then please share it will be great thankfull to you
IMO, you are approaching this wrong. What exactly are you trying to hide? What is it about your JS code that you feel the need to lock down?
i need to hide my all business logic that is present in the js file say eazywallet.js so that the third person cannot read this after extracting it
Which javascript library?? I tried with zip.js .. But thta is giving errors . I think we cannot create zip file in sdcard using file api ?? AM i correct??
And also i am getting error code 1 in file transfer api of phonegap. I am inside particular directory and uploading the file from there to server. It is giving file not find error , but the file exists..
ok i sorted out that file not found exception . There was error in server side. But now my question is , is it possible to transfer the file in synchrounous way through this api?? As i have lots of file , asynchronous will end in error foe some files.
If by transfer you mean upload, yes. Check the PG docs for the upload method.
ok... thanks... Can u suggest me with zip libraries. I tried with zip.js .It says blob support is not there for android
Sorry, nope, that's the only one I've used.
Hi Raymond,
need to browse audio files from a phonegap app. Like getPicture method for browse image files from device is there any way to browse audio files from device?
Thanks in advance.
You would use the directory listing logic and just filter by audio files, ie, .mp3 (or other extensions).
I'm a little bit confused.
At HTML5Rocks FileSystem API's is spoken of and PhoneGap/Cordova has an File API.
What's the difference between those?
Why should I need PG File API if HTML5 uses it's own FileSystem API?
Cheers, Timo
The HTML5 FileSystem API is *not* supported on mobile (http://caniuse.com/#search=... outside of Chrome and Blackberry. Chrome is still pretty small on Android (versus the stock Android browser).
Differences: I don't have a point by point comparison. I think they are similar.
I am using the fileWriter as described to archive geolocation data on the user's device. How would I implement this to write the file to something like data/{packageName}?
Instead of the so card of course...
Why would you use fileWriter for geolocation? You don't need that. Are you trying to store their location? If so - you wouldn't want to store it on the file system. I'd store it in localStorage instead.
I actually do store it in localStorage then at giving intervals I dump it to a db file on the coldfusion server for location history in the future. I was wanting to do something similar client side for offline use.
Well, I still think a physical file is overkill for this, but, you could write to a file. You mentioned writing to data/something as opposed to the sd card. As far as I know, you can write to any directory you have Write access too. What did you try and how did it fail?
Hi Raymond, I've been struggling with setting up my File plugins despite following your instructions. Been encountering one error after another. Can I have a sample full project for Android?
Are you asking for a full project that uses the file api? I don't have one. All I can suggest is to start a new project (so you have a clean, fresh start) and try the File API there. Use the sample code from the docs. This post may be helpful too:
http://www.raymondcamden.co...
Hi Raymond, so I'm using phonegap 3.0 for a big project and I tried the sample code from your example. I did a little bit of tweaking due to some differences in phone gap 3.0 setup. I'm getting this error "Uncaught ReferenceError: LocalFileSystem is not defined".
I posted my codes for my LocalFileSystem.js and cordova_plugins.js. I got my LocalFileSystem file from the Phonegap website.
LocalFileSystem.js
exports.TEMPORARY = 0;
exports.PERSISTENT = 1;
-------------------------------------
cordova_plugins.js
cordova.define('cordova/plugin_list', function(require, exports, module) {
module.exports = [
{
"file": "plugins/org.apache.cordova.file/www/FileSystem.js",
"id": "org.apache.cordova.core.file.FileSystem",
"clobbers": ["window.FileSystem"]
}
]
});
Well, I'm not sure what you mean when you say you got code from the phonegap web site. You should be installing the plugins via the command line instead.
Yes that's what I mean. I installed via command line. Sorry if my question wasn't clear. I tried reverting back to phonegap 2.7.0 and it worked! I'm not exactly sure why the latest version isn't working very well. Thank you very much by the way :)
Ok, well, if you want to try latest again, let me know.
This is very nice for me. But readfile is not work. Please help
How did it not work? What error did you get?
Hi,
I am trying to run this code on ios simulator. readFS function displays the path as "/" and then it stops and does nothing. It does not call the readFileEntry method. Even the readfail does not displays anything. Any idea on this??
....
function onDeviceReady(){
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, readFS, readfail);
}
function readFS(fileSystem) {
alert(fileSystem.root.name);
fileSystem.root.getFile("abc.txt", null, readFileEntry, readfail);
}
...
Thanks
You may have a bug in your code. For me readfail ran with code 1 which is file not found. As I don't have this file, it makes sense.
As an FYI, adding {create:true} for the second argument makes it work - again - as expected.
What i am trying to do is have an html file on my local server and access it on Android and iOS device. In this html file i am loading the appropriate cordova.js file for the platform and then reading the file content from the device. The same code works for me when i run it on android device but doesn't work when i run it on ios simulator (since i don't have an ios device right now).
I am using the js files that comes in phonegap-2.9.0.zip in android and ios folders respectively. I have downloaded phonegap from http://phonegap.com/install/ and have created the ios project using CLI and also added the plugins the same way.
I have pasted the contents of config.xml and the html file here http://pastebin.com/vHJGpZ10
If you see ann bug in my code, please let me know.
"What i am trying to do is have an html file on my local server and access it on Android and iOS device."
By "local" server I assume you mean a web server, not local to the device?
" In this html file i am loading the appropriate cordova.js file for the platform and then reading the file content from the device. "
So your PG app loads *another* file w/ cordova.js included? Seems risky to me. I've seen people do this in the past but I don't recommend it. I also think Apple notices this and will block you from releasing it on the store.
So if I understand you right... I can't really help as this isn't something I think is supported or recommended.
Finally, PG 2.9 is now old. I'd recommend switching to 3.4.
Hi Raymond
I try want to build a simple Filehandler to select a folder.
My Code is running an gives me a List of the "Home Dir"("file:///storage/emulated/0/").
Now i want to have a list from all entries in the subdirectory "Download"
("file:///storage/emulated/0/Download").
But I always get the list from my "Home Dir"?
here my code
function Myft_GetFileSystem(fncCallBack, url){
if(!AppRunsNative()) return;
url = "file:///storage/emulated/0/Download/";
console.log('request fs for:' + url);
window.resolveLocalFileSystemURL(url,
function(entry) {
// Success
// create directory reader
var directoryReader = entry.filesystem.root.createReader();
console.log('request dir for:' + entry.nativeURL + " is Dir:"+ entry.isDirectory );
var readEntries = function() {
// get a list of all entries in the directory
directoryReader.readEntries(
function(results) {
if (!results.length) {
if(fncCallBack!=null){fncCallBack(Myft.entries);}
} else {
Myft.entries = Myft.entries.concat(toArray(results));
readEntries();
}
},
function(evt){ // error get file system
alert("File System Error3: "+evt.target);}
);
};
Myft.entries = [];
readEntries(); // Start reading dirs.
},
function(error) {console.log('fail 4');}
);
}
Hi Raymond
I found the bug
var directoryReader = entry.createReader();
( NOT : var directoryReader = entry.filesystem.root.createReader();)
and now it works.
Have fun Thomas
Such a great article! helped me clean up the mess in my head :) I just have one quick question, I've seen here and other documentations the Phonegap File API it's an implementation of the HTML 5 one, however, the requestFileSystem method in HTML 5 implementation asks for the number of bytes to be requested, and your examples uses a 0 on that parameter, I've seen some gists and even the cordova documentation using 0 as the size parameter, does it works in a special way?
You know - I don't know. I checked the FS spec thinking that maybe 0 means, "We don't know", or "No limit", and that isn't indicated. Honestly - you got me.
Plz tell me how to display content of file on the screen
Use the FileReader API. To add content to the DOM, vanilla JS will do it for, and jQuery of course.
can u do it for me Please
doReadFile Doesn't work
Sorry, no, this is not what I do here. I try my best to help, give guidance, etc, but I do not write code for you.
Am I able to use the File API to write the contents of my webSQL database to the sd card and then retrieve it in iOS? For example I write to SD card, uninstall the app, install it again and then import the websql database back to the app and all the content from before uninstalling would be there. I'm struggling to wrap my head around the documentation
In theory the answer is yes. The File API lets you read and write. This blog post shows you how you could get a string version of the db. The issue is - is there a place in the iOS file system you can write to, a global space, like /Documents, that you could read from again in the future. That part I'm not sure of and you would need to do some research.
Well there is a var/mobile/Documents folder. I can only access that manually because I have jailbroken my iPhone, but I suppose reading and writing wouldn't need manual access anyway
Hi, GREAT tutorial :)......
but When I paste your code as is... I get a "object Object" error on android...
I installed your .apk file and it works perfectly.....am I missing something...
I also installed :
cordova plugin add org.apache.cordova.file
but still I get the : Error [object Object]
I'm using phonegap 3.5
TANKS IN ADVANCE
What line throws that message?
Thanks for the quick reply...
its the start of the onFSSuccess(fs) Function.
I think it douse not go further than that.
I tried the following :
console.log(fileSystem);
*
But I get the following Error:
[phonegap] [console.log] processMessage failed: Message: S01 File163549358 {"roo
t":{"fullPath":"\/","filesystemName":"persistent","isDirectory":true,"nativeURL"
:"file:\/\/\/storage\/emulated\/0\/","filesystem":1,"isFile":false,"name":""},"n
ame":"persistent"}
[phonegap] 200 /socket.io/?EIO=2&transport=...
K1S7X6MR1AAAD
[phonegap] [console.log] processMessage failed: Error: RangeError: Maximum call
stack size exceeded
[phonegap] [console.log] processMessage failed: Stack: undefined
[phonegap] 200 /socket.io/?EIO=2&transport=...
K1S7X6MR1AAAD
*
Wow, not sure what to say there. socket.io - are you using websockets? How are you testing?
It came from phongap cmd:
I am using "phonegap serve" command too build on the fly...
Your issue seems to be with the websocket communication used by phonegap serve. It has nothing to do (afaik) with the File system. What I'd suggest is filing a bug report with the PhoneGap CLI so they can help. Need that URL? https://github.com/phonegap...
:), Will do.
Thanks again for the quick response and help.
Awesome tutorial
How do you run this on the latest version ? Do i just replace
src="phonegap-1.4.1.js" with src="cordova.js" ?
Seems not too work for me...NB: there's a android bug, has it been fixed ?
No - see the documentation - you can upgrade a platform via the CLI. I rarely do this so I can't remember the exact call, but it is in the CLI documentation.
Thanks, :), answered my questions perfectly....
would of expected it too be fixed on the latest version :/...
I can't use SQL because of the lack of compatibility across devices,
I am testing on the android 4.3- guess I will have too go the Json rout to store data...
You sure you can't use WebSQL? It is pretty well supported on mobile.
It's for older devices, and there is not much documentation and samples out there... I am now doing it with
PhoneGap + Yeoman + AngularJS + Ionic
and using the AngularJS of saving simple data
WebSQL has been around for a while now - I'd check at caniuse.com. It may be supported for what you want to cover.
Wow, thanks, I did not know a site like existed, Will have too do further research, thanks
Hey Raymond, I'm having an issues with doReadFile,
Here are little details about my working environment
- I am using Cordova 5.+ (without PhoneGap)
- Debugging the app with 'ionic run android' by USB debugging
Issues
-fileSystem gave an error as undefined, so i changed it to LocalFileSystem (No problem here, Seems solved)
-The code :
function doReadFile(file){
LocalFileSystem.root.getFile(file, {create:true}, readFile);
}
gives me the following error
Error in Success callbackId: File1213672858 : TypeError: Cannot call method 'getFile' of undefined
error Uncaught TypeError: Cannot call method 'getFile' of undefined, http://192.168.0.102:8100/cordova.js, Line: 312
What i really want to do is load an image file from filesystem and get the datauri(base64) of the file.
Searching for solutions, Thank you.
Are you sure you added the plugin?