Visit our archive

mobileStorage Local File Access / Editing for Android and iOSRecently, while building The Etiquette App in Flash Builder for both Android and iOS we decided that the best way to allow for data updates was to store the data files locally and intermittently check for updates on our server. If there is a data update, the app checks for a connection, loads the new data and completes the update as needed.

While trying to make this all work, we ran into a number of issues. The biggest issue was just accessing the files and making sure that the application was able to access files on both Android and iOS. We searched the web and were able to find things that worked for one mobile OS but not the other. It was a mind-numbing experience to say the least.

When working with mobile in Air, there are a couple of different directories that you have access to through your code. The main two that we are going to focus on are the applicationDirectory and the applicationStorageDirectory. What we discovered was that the applicationDirectory is read-only, which is unfortunate because that is where the files that you include with your application reside. You can’t have an app that updates the files that come with it.

As a bit of a workaround, we came up with a method that has worked quite well for us. That is to include the data files in the applicationDirectory and, on the first launch of the app, we copy them to the applicationStorageDirectory so that they are now workable. This does duplicate data, but it isn’t too much data and we wanted to make sure that data shipped with the app so we didn’t force users to be online while using it.

Below is a walk-through of how we accomplished this:

// check to see if the file exists in the applicationStorageDirectory
if (!File.applicationStorageDirectory.resolvePath("data/filename.dat").exists) copyFile("filename.dat");

private function copyFile(ARG_file:String): void {

	// check to see if the file exists
	var file:File;
	file = File.applicationStorageDirectory.resolvePath("data/" + ARG_file);

	// if it doesn't exist
	if (!file.exists) {	
		// get the file that we included with the application	
		var originalFile:File;
		originalFile = File.applicationDirectory.resolvePath("data/" + ARG_file);

		// open and “READ” the content of the file
		var fileStream:FileStream = new FileStream();
		fileStream.open(originalFile, FileMode.READ);
		var fileContent:String = fileStream.readUTF();
		fileStream.close();

		// create the new file	
		var applicationStorageDirectoryPath:File = File.applicationStorageDirectory;
		var nativePathToApplicationStorageDirectory:String = applicationStorageDirectoryPath.nativePath.toString();
		nativePathToApplicationStorageDirectory += "/data/" + ARG_file;
		file = new File(nativePathToApplicationStorageDirectory);

		// write the contents from the other file to the new file
		var writeStream:FileStream = new FileStream();
		writeStream.open(file, FileMode.WRITE);
		writeStream.writeUTF(str);
		writeStream.close();
	}
}

That code executes every time the application is loaded. From then on, we just use the file location in the applicationStorageDirectory to read the data from. When there is an update from the server, it just updates the new file. You can use the same code above to read the new file.

private function readFile(ARG_file:String):String {
	// get the file that we included with the application	
	var file:File = File.applicationStorageDirectory.resolvePath("data/" + ARG_file);

	// open and “READ” the content of the file
	var fileStream:FileStream = new FileStream();
	fileStream.open(originalFile, FileMode.READ);
	var fileContent:String = fileStream.readUTF();
	fileStream.close();
	return fileContent;
}

private function writeFile(ARG_file:String, ARG_content:String):void {
	// find the file 	
	var applicationStorageDirectoryPath:File = File.applicationStorageDirectory;
	var nativePathToApplicationStorageDirectory:String = applicationStorageDirectoryPath.nativePath.toString();
	nativePathToApplicationStorageDirectory += "/data/" + ARG_file;
	file = new File(nativePathToApplicationStorageDirectory);

	// write the contents parameter
	var writeStream:FileStream = new FileStream();
	writeStream.open(file, FileMode.WRITE);
	writeStream.writeUTF(ARG_content);
	writeStream.close();
}

That’s it! You are now able to read and write files on both Android and iOS using Actionscript 3 in Air without difficulty.

stumbleupon Local File Access / Editing for Android and iOSshare save 171 16 Local File Access / Editing for Android and iOS
  • Francesco Nov 21, 2011

    Hi,
    thank you for the post! I’m trying to handle a local file in a Flex application for iOS devices.
    I tried to use the way you suggested:
    var image:File = File.applicationDirectory.resolvePath(“data/res.png”);

    but the method image.exists returns FALSE.

    The file path is {FLEX_PROJECT_FOLDER}/data/res.png and the class that use it is placed in {FLEX_PROJECT_FOLDER}/views/View.mxml

    Can you help me to place the file in the right way?

    Thank you in advance,
    Francesco

  • cultcreative Nov 21, 2011

    the “data” folder needs to be in the /bin (or bin-release/bin-debug) folder. I generally just add the data folder to my /src folder so that it gets added to the build in Flash Builder no matter where it is building to. does that make sense?

  • Francesco Nov 23, 2011

    Thank you for the support!
    If I put the images in the src folder, after building with Flash Builder, I can use them with File.applicationDirectory.resolvePath(“image.png”). :)

  • William Dec 23, 2011

    Hey, I am getting an error on line 31, the (str) is undefined…any idea what I need to set this as?

    Regards,
    William

  • cultcreative Dec 23, 2011

    Hey, sorry about that, i think that it should be writeStream.writeUTF(fileContent);

  • Adam Jan 14, 2012

    I am attempting to load a local PDF document in my application in AIR 3.1 for iOS. I have only been able to get the file to load using the StageWebView class,however I really want it to open in the native application on the device, such as Adobe Reader, or even the web browser. The PDF has to be local, so putting the file up on a server is not an option for me. I have used your example above to copy the file to my Application Storage Directory, however I am getting an error when it attempts to load the copied file, “The file “file_name.pdf” could not be opened. It may be damaged or use a file format that Preview doesn’t recognize.” I am seeing the file show up in the application storage directory when running this on my computer and it even opens the correct program called Preview. The method I am using on the file class to open the file is openWithDefaultApplication(). When I put the .ipa over to my device I am tracing out the location in a debug text field I put on the stage and it looks correct, however it doesn’t switch to the reader application. So I am wondering if my copy is broke on the device as it also is on my computer. Any input/advice on this would be greatly appreciated.

    Thanks.

    Adam

  • cultcreative Jan 14, 2012

    I have a feeling that it might have something to do with where you are trying to open the file from. Adobe says: “You cannot use the openWithDefaultApplication() method with files located in the application directory.” (http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118666ade46-7fe4.html#WS2A7C0A31-A6A9-42d2-8772-79166A98A085), so you would have to copy it somewhere first. Try something like the following, where you create a duplicate instance and load that:

    var tempFile:File;
    file.copyTo(tempFile);
    tempFile.openWithDefaultApplication();

  • Adam Jan 15, 2012

    So I went ahead and did something:

    var buildPath:String = File.applicationDirectory.nativePath;
    var source:String;
    source = buildPath + “/file_name.pdf”;
    var file:File = new File(source);

    var destination:File = File.applicationStorageDirectory;
    destination = destination.resolvePath(“adamzucchi_resume.pdf”);

    try {
    file.copyTo(destination, true);//I also tried file.moveTo(destination, true);
    destination.openWithDefaultApplication();
    debug_txt.appendText(destination.url + ‘\n’);
    }
    catch (error:Error) {
    trace(“Error:” + error.message);
    debug_txt.appendText(‘Error = ‘ + error + ‘\n’);
    }

    I attempted both a moveTo and a copyTo to the applicationStorageDirectory. Both of these work fine on my desktop, however when moving it to the iPad, I get two errors, one for each of the methods. The moveTo() throws the following error, Error #3001. The copyTo() throws the following error, Error #0.

    I am wondering if I have to create a temp directory?

  • cultcreative Jan 15, 2012

    what I think you should be doing is more along the lines of

    var buildPath:String = File.applicationDirectory.nativePath;
    var source:String;
    source = buildPath + “/file_name.pdf”;
    var file:File = new File(source);

    try {
    var tempFile:File();
    file.copyTo(tempFile);
    tempFile.openWithDefaultApplication();
    }
    catch (error:Error) {
    trace(“Error:” + error.message);
    }

  • Adam Jan 15, 2012

    I should also mention that in my above post the strings that contain the file path were meant to be the same :) They were when I compiled, I just didn’t know if it was necessary to leave the real filename in.

    Thanks again.

  • Adam Jan 15, 2012

    With your try/catch I am getting the following error: Error:Error #2037: Functions called in incorrect sequence, or earlier call was unsuccessful.

  • cultcreative Jan 15, 2012

    woops, sorry.. change var tempFile:File() to var tempFile:File = new File();

  • Adam Jan 15, 2012

    Sorry I should have mentioned that I had done that. I got a compiler error with what I copied and changed that constructor to fix that.

  • cultcreative Jan 15, 2012

    in that case, there are a couple other options that I can think of.

    1) are you sure you are including the pdf file in the package? are you getting a true value when you check if the original file exists while debugging on the device? if so move onto number 2.

    2) This really should work, it is much like your original post

    var buildPath:String = File.applicationDirectory.nativePath;
    var source:String;
    source = buildPath + “/file_name.pdf”;
    var file:File = new File(source);

    if (!file.exists) {
    debug_txt.appendText(‘The file you are looking for does not exist in the application directory \n’);
    return; // assuming you are using this in a function that would return void.
    }

    try {

    var newfile:File;
    newfile = File.applicationStorageDirectory.resolvePath(“file_name.pdf”);

    file.copyTo(newFile);//I also tried file.moveTo(destination, true);
    newFile.openWithDefaultApplication();
    debug_txt.appendText(newFile.url + ‘\n’);
    }
    catch (error:Error) {
    trace(“Error:” + error.message);
    debug_txt.appendText(‘Error = ‘ + error + ‘\n’);
    }

  • Adam Jan 15, 2012

    I am including the pdf, and I am getting that #0 error again (Error:Error #0). I have narrowed it down and it is breaking on this line:

    newFile = File.applicationStorageDirectory.resolvePath(‘/file_name.pdf’);

  • cultcreative Jan 15, 2012

    the only thing that i could see being an issue with that is that you have an extra slash in your line, where mine is just the file name. You could also try the method that I used in the code above

    var applicationStorageDirectoryPath:File = File.applicationStorageDirectory;
    var nativePathToApplicationStorageDirectory:String = applicationStorageDirectoryPath.nativePath.toString();
    nativePathToApplicationStorageDirectory += “filename.pdf”;
    newFile = new File(nativePathToApplicationStorageDirectory);
    file.copyTo(newFile);
    newFile.openWithDefaultApplication();

  • Adam Jan 15, 2012

    I tried it both with and without the ‘/’ in front of the filename. I have incorporated the code in your last comment and it is breaking again on that first line:var applicationStorageDirectoryPath:File = File.applicationStorageDirectory;

  • Adam Jan 15, 2012

    So I nested in to where this applicationStorageDirectory actually is on my machine and deleted it, and now I can actually compile. I am wondering if something was corrupt in that? I will compile for the device and let you know how this goes :) Thanks again for all the help, I really appreciate it.

  • Adam Jan 15, 2012

    So here is the outcome. I have been able to get it to compile on my machine, and it opens with my default application, Preview, which is great. However after pushing this onto the device, I am getting that Error #0 in my debug_txt again. This applicationStorageDirectory is available when developing for iOS right?

  • Adam Jan 15, 2012

    So I put it on the device and put a series of debug_txt traces in after each line in the try block and it is breaking on this: applicationStorageDirectoryPath.openWithDefaultApplication();

    Is this just not something we can call on iOS? I am compiling with AIR 3.1.

  • Jesse Feb 2, 2012

    Adam! -Did you ever get this to work when publishing to AIR 3.1 for iOS, or did you give up?? Also, “cultcreative” I want to do what Adam is doing but, I do want to check against a .pdf on a server. I am using Flash CS5.5 to publish, not Flash Builder -is that a problem?

  • cultcreative Feb 6, 2012

    Jesse, it shouldn’t matter what development environment you are using. I would assume that you should be able to open the PDF the same way that Adam and I have been working through. Give it a try and feel free to comment and let me know what happens. Take a look at http://www.adobe.com/devnet/air/flex/articles/exploring_file_capabilities.html for some reference with openWithDefaultApplication.

  • James Vanderbilt Feb 22, 2012

    I am trying to do this same thing on Android and am having problems. I’ve given up using openWithDefaultApplication(), since apparently you can do that on a file that is stored with your app. I’m trying a workaround where I copy the PDF to a temporary location and open it from there. But again, I’m having problems. The file appears to copy fine (no errors or events thrown), but the copy of the file doesn’t seem to actually get created. So when I do openWithDefaultApplication, I get a 3003 error or file/directory not found. This is on Android. Any ideas?

  • Paul Steven Apr 17, 2012

    Does your code create the “data” folder in the applicationsStorageDirectory if it doesn’t already exist?

  • [...] reading an item on the cultcreative blog site called Local File Access/ Editing for Android and IOS, I had modified the app so if there was no internet connection available, the app would still work [...]

  • URL Dec 14, 2012

    … [Trackback]…

    [...] Find More Informations here: cultcreative.com/tutorials/11/13/2011/local-file-access-editing-for-android-and-ios [...]…

  • Rafa Dec 15, 2012

    Hi people. I hace done the same with an mp3 file and no problems I can listen perfectly without errors. I would like to ask you a question fellas…. How would you do for searching in android, all the files that in the device exist?? I have the problem that I don’t get a root path and i don’t know how to explore all the directories to find the mp3 files…. Any advice or idea??

  • Caio Dec 18, 2012

    I’m trying the same problem, I wonder if they found a solution to this problem in android

  • Adam M. Feb 21, 2013

    Hello there. I’ve been doing Flash Pro for about two years now so I still consider myself a newbie. What i’m trying to accomplish is in my iOS app it calls to an XML file my server and displays the text in the text fields i have on stage. That parts simple. But now I want to store my current XML on the server to the applicationStorageDirectory for off line use. When I stumbled upon this tutorial i felt this is exactly what I’m looking for. How ever I can’t seem to get this to work. I never worked with the filesystem class before.

    Questions

    1. Where would i put the URL path to the file on my server.

    2. I figure your code is more of a starting point and not so much drop in and go, so to accomplish what i want, what more work do I add so I research that to do.

    3. And once i can get that app reading the file storage directory would the parsing be any different than how i do it from loading the XML by URL request. Like this.

    AS3
    var myXML:XML=new XML(e.target.data);
    textBox1.text=myXML.sampleText1;

    XML
    My Text

    Please let me know if you can offer any advice, Thanks

  • Name (Required)

  • Email (Required, but not published)

  • Url (Optional)

  • Comment (Required)

*

*