I need to read a text stream by using StreamReader from file on android platform. File is about 100k lines, so even editor is getting stuck if i try to load it all to TextAsset or if i use WWW.
I simply need to read that file line by line without loading it all to a string. Then i'll do a tree generation from the lines that i got from the file. (But probably that part doesn't matter, i just need help on file reading part.)
I'm giving the code that i wrote down below. It works perfectly on editor, but fails on android.
I would be glad if anyone tell me, what am i missing.
(ps. english is not my native and this is my first question on the site. so sorry for the any mistakes that i may have done.)
private bool Load(string fileName)
{
try
{
string line;
string path = Application.streamingAssetsPath +"/";
StreamReader theReader = new StreamReader(path + fileName +".txt", Encoding.UTF8);
using (theReader)
{
{
line = theReader.ReadLine();
linesRead++;
if (line != null)
{
tree.AddWord(line);
}
}
while (line != null);
theReader.Close();
return true;
}
}
catch (IOException e)
{
Debug.Log("{0}\n" + e.Message);
exception = e.Message;
return false;
}
}
You can't use Application.streamingAssetsPath as a path on Android because streaming assets are stored within the JAR file with the application.
From http://docs.unity3d.com/Manual/StreamingAssets.html:
Note that on Android, the files are contained within a compressed .jar
file (which is essentially the same format as standard zip-compressed
files). This means that if you do not use Unity’s WWW class to
retrieve the file then you will need to use additional software to see
inside the .jar archive and obtain the file.
Use WWW like this in a coroutine:
WWW data = new WWW(Application.streamingAssetsPath + "/" + fileName);
yield return data;
if(string.IsNullOrEmpty(data.error))
{
content = data.text;
}
Or, if you really want to keep it simple (and your file is only a few 100k, stick it in a resource folder:
TextAsset txt = (TextAsset)Resources.Load(fileName, typeof(TextAsset));
string content = txt.text;
Related
I am new to Unity and Android development, but please do not mark this as a duplicate - I've looked all over the stacks for an answer to this, but similar topics and questions haven't yielded any working solutions, or are wanting on details, or outdated, or seem not to fit my specific need.
Ok, I have a quiz app built from following part 1 & 2 of this tutorial. That link contains all the source files anyone might need for reference, and everything works fine on ios and in the unity editor, fyi.
The trouble is with the loadGameData function from the DataController script in Android. Again, everything works fine in iOS and the unity editor, but when I make an Android sdk, the quiz is blank and the console says the data couldn't be loaded.
Here is how the function is currently written (full file in tutorial link):
private void LoadGameData ()
{
string filePath = Path.Combine (Application.streamingAssetsPath, gameDataFileName);
if (File.Exists (result))
{
string dataAsJson = File.ReadAllText (result);
GameData loadedData = JsonUtility.FromJson<GameData> (dataAsJson);
allRoundData = loadedData.allRoundData;
} // #if(File.Exists...
else
{
Debug.LogError ("Cannot load game data!");
} // #else
} // #LoadGameData
If you check the same tutorial on youtube, you'll see lots of people have noted the same problem with the Android build and have been left unanswered. Same goes with unity forums - that's one reason why I don't think this question is a duplicate and could be helpful to others in a similar situation.
I've found that Android has always been sorta tricky with this and that there used to different ways of accessing a file based on platform, but these days "Application.streamingAssetsPath" should find the streaming assets directory on any platform, even Android.
What I've also learned is that in android, even if the path is correct, the file is compressed and will only return a url. So the url needs to be converted using unity's WWW class. I get that, but as of yet, I haven't been able to re-write my loadGameData function to work properly and load the allRoundData array.
Here's an example of some things I've tried:
IEnumerator androidData()
{
string filePath = Path.Combine (Application.streamingAssetsPath, gameDataFileName);
if (filePath.Contains("://"))
{
WWW www = new WWW(filePath);
yield return www;
result = www.text;
} // #if(filePath.Contains
} // #androidData
private void LoadGameData ()
{
androidData();
if (File.Exists (result))
{
string dataAsJson = File.ReadAllText (result);
GameData loadedData = JsonUtility.FromJson<GameData> (dataAsJson);
allRoundData = loadedData.allRoundData;
} // #if(File.Exists...
else
{
Debug.LogError ("Cannot load game data!");
} // #else
} // #LoadGameData
I know I'm close, and this is probably simple -- but I just can't seem to get to the finish line on this. Can some one help me figure out how to write the loadGameData function so it will load this allRoundData array on android?
An example code would be awesome and much appreciated, not just by me, but I'm sure many others would appreciate it also - Thank you!
UPDATE:
Based on the first answer, I've tested some code that works on the unity editor, but crashes in Android. In the Unity editor, I get the "file already exists" message. Here is the code I've tested:
Already had: private string gameDataFileName = "data.json";
I added the copyFile call above loadGameDate in Start() and wrote the copy file and loadGameData functions like so ..
int copyFileToPersistentDataPath(string gameDataFileName)
{
string persistentPath = Application.persistentDataPath + "/" + gameDataFileName;
try
{
//Copy only if gameDataFileName does not exist
if (!System.IO.File.Exists(persistentPath))
{
string path = System.IO.Path.Combine(Application.streamingAssetsPath, gameDataFileName);
WWW www = new WWW(path);
while (!www.isDone) { }
System.IO.File.WriteAllBytes(persistentPath, www.bytes);
Debug.Log(gameDataFileName + " Successfully Copied File to " + persistentPath);
return 1;
}
else
{
Debug.Log(gameDataFileName + " File already exist here. There is no need to copy it again");
return 0;
}
}
catch (Exception e)
{
Debug.Log(gameDataFileName + " Failed To Copy File. Reason: " + e.Message);
return -1;
}
}
private void LoadGameData ()
{
string tempPath = Path.Combine(Application.persistentDataPath, gameDataFileName);
string dataAsJson = File.ReadAllText(tempPath);
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
allRoundData = loadedData.allRoundData;
} // #LoadGameData
This works with or without the call to copy the file in the editor, but crashes either way in Android.
I ended up putting the files in a Resources folder and going the resources.load a json file into a text asset and throw that into a string to parse route. I now have two different load functions, one that works in ios etc. and one that works in android. Here is the android function (the resourcesGameDataFile does not have the .json extension):
public void LoadDataForAndroid()
{
TextAsset dataFile = Resources.Load(resourcesGameDataFile) as TextAsset;
string dataAsJson = dataFile.ToString();
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
allRoundData = loadedData.allRoundData;
Debug.Log ("Android data loaded with" + resourcesGameDataFile);
} // #LoadDataForAndroid
And this works in the unity editor and in Bluestacks (android simulator).
As for loading and saving games, this is a duplicate. I marked and remove this as a duplicate because the answer in the duplicated questions did not explain how to read from the StreamingAssets folder. It only talked about saving and loading data.
Make sure that Write Permission is set to External (SDCard).
The first thing to do is to copy the file from StreamingAssets to the persistentDataPath location.
I've found reading data from Application.streamingAssetsPath problematic but I use two methods to solve this.
int copyFileToPersistentDataPath(string fileNameWithExtensionName)
{
string persistentPath = Application.persistentDataPath + "/" + fileNameWithExtensionName;
try
{
//Copy only if fileNameWithExtensionName does not exist
if (!System.IO.File.Exists(persistentPath))
{
string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileNameWithExtensionName);
WWW www = new WWW(path);
while (!www.isDone) { }
System.IO.File.WriteAllBytes(persistentPath, www.bytes);
Debug.Log(fileNameWithExtensionName + " Successfully Copied File to " + persistentPath);
return 1;
}
else
{
Debug.Log(fileNameWithExtensionName + " File already exist here. There is no need to copy it again");
return 0;
}
}
catch (Exception e)
{
Debug.Log(fileNameWithExtensionName + " Failed To Copy File. Reason: " + e.Message);
return -1;
}
}
If that does not work for you, use the method with WebClient below:
void copyFileToPersistentDataPath(string fileNameWithExtensionName)
{
string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileNameWithExtensionName);
string persistentPath = Application.persistentDataPath + "/" + fileNameWithExtensionName;
Debug.Log("Dir: " + persistentPath);
WebClient webClient = new WebClient();
webClient.Proxy = null;
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadComplete);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnUpdateDownloadProgress);
Uri uri = new Uri(path);
webClient.DownloadFileAsync(uri, persistentPath);
}
void OnDownloadComplete(object sender, AsyncCompletedEventArgs e)
{
Debug.Log("Finished Downloading: " + e.Error.Message);
}
void OnUpdateDownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
Debug.Log("Uploading Progreess: " + e.ProgressPercentage);
}
File Copy Usage:
copyFileToPersistentDataPath("yourFileName.txt");
After copying the file, you can then read and convert it to Json like this:
string fileNameWithExtensionName = "questionfile.txt";
string tempPath = Path.Combine(Application.persistentDataPath, fileNameWithExtensionName);
string dataAsJson = File.ReadAllText(fileNameWithExtensionName);
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
Is it possible to load strings.xml from sd card instead of application res/values/... Search on the web but didn't find any tutorials. My thought is download the xml to sd card then save the strings element to an array.
public void stringsxml(){
File file = new File(Environment.getExternalStorageDirectory()
+ ".strings.xml");
StringBuilder contents = new StringBuilder();
try {
//use buffering, reading one line at a time
//FileReader always assumes default encoding is OK!
BufferedReader input = new BufferedReader(new FileReader(file));
try {
String line = null; //not declared within while loop
/*
* readLine is a bit quirky :
* it returns the content of a line MINUS the newline.
* it returns null only for the END of the stream.
* it returns an empty String if two newlines appear in a row.
*/
while (( line = input.readLine()) != null){
contents.append(line);
contents.append(System.getProperty("line.separator"));
}
}
finally {
input.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
String data= contents.toString();
}
Well, actually it is semi-possible, but you have to create a derivate LayoutInflater which will replace string codes with thus read strings.
I have documented my attempts and failings together with initial implementation here.
Summary: simple strings work, string arrays do not
No, this is not possible. Check Android decoumentation about resources:
The Android SDK tools compile your application's resources into the application binary at build time. To use a resource, you must install it correctly in the source tree (inside your project's res/ directory) and build your application.
Resources are built-in into the application binary and you can't read them from a file.
i am using a properties file to get the url of various webservices i am calling from my android. I want to provide a congiguration option so that ip address for web service can be modified.
how to proceed ?
i have a resource folder in src folder which have the following values
update=http://10.52.165.226:50000/android/rest/get/updateSfc
ShopOrder=http://10.52.165.226:50000/android/rest/getShopOrder/bySite?site=
i am using resource bundle to use this values in android.?
I am thinking of reading the file and replace all occrence of Ip address. how to rad the properties file and edit it in android
Here is a complete solution for you to use .properties file in your project.
1 Create a file named app.properties in assets folder of your android project
2 edit the file and write with in properties that you want to use for example as
test=success
And Save file
3 Write this Method with in your Activity Class
private Properties loadPropties() throws IOException {
String[] fileList = { "app.properties" };
Properties prop = new Properties();
for (int i = fileList.length - 1; i >= 0; i--) {
String file = fileList[i];
try {
InputStream fileStream = getAssets().open(file);
prop.load(fileStream);
fileStream.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "Ignoring missing property file " + file);
}
}
return prop;
}
4 With in OnCreate Method write some thing like this
Properties prop = null;
try {
prop = loadPropties();
} catch (IOException e) {
Log.e(TAG, "Exception", e);
}
Toast.makeText(getApplicationContext(), "Result " + prop.getProperty("test"),
Toast.LENGTH_LONG).show();
5 add necessary imports
Hope this helps :)
Read about Data Storage in Android and more specifically Shared Preferences. For more complete usage of saving user preferences, read about the PreferenceActivity.
A tutorial on using Shared Preferences can be found here
Resources, Assets and other files/folders that form the part of Apk cannot be modified.You can use a database for depending on nos of rows that you will use
I have some different language html file for different language environment, and all of those html files contain images. Now I want to show suitable html files with WebView adapt to current language of Android. How can I do that? Thanks.
You can give special names for non-English files like file-de.html for German and then use the following code:
private static String getFileName() {
Configuration config = getResources().getConfiguration();
InputStream stream = null;
try {
String name = "file-" + config.locale.getLanguage() + ".html";
stream = getAssets().open(name);
return name;
} catch (IOException exception) {
return "file.html";
} finally {
if (stream != null) {
stream.close();
}
}
}
You might use "string values" to get Android language dependent HTML file names without using any additional java code and just using getString(). Here's an example for 2 languages:
In file values/strings.xml:
<string name="html_help_basepage">en_help.html</string>
In file de/strings.xml:
<string name="html_help_basepage">some_german_help.html</string>
And so on...
For your Java code (loading from local file in asset folder as in question)
helpView = (WebView) findViewById(R.id.wv_help);
String adaptedToLanguage = getString(R.string.html_help_basepage)
helpView.loadUrl("file:///android_asset/" + adaptedToLanguage);
No additional Java code is needed. I hope this is helpful and answers the question.
Added a new answer, which should be more precise by providing an example.
I'm trying to develop a small application, where I was given a JSON file, and I have to extract data from it. As I understood a JSON object takes a string argument, thus I'm trying to access a file and write the data from it to a string.
I've placed that file in a "JSON file" folder, and when I try to read the file, it throws me a file not found exception.
I've tried several ways to find a path to that file, but every attempt was for vain.
It might be that I'm extracting the path wrong, or might be something else, please help me.
Thanks in advance.
here is the code of finding the path:
try
{
path = Environment.getRootDirectory().getCanonicalPath();
}
catch (IOException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
File jFile = new File(path + /"JSON file/gallery.json");
here is the code for reading from a file :
String str ="";
try
{
BufferedReader in = new BufferedReader(new FileReader(jFile));
while ((str += in.readLine()) != null)
{
}
in.close();
}
catch (IOException e)
{
e.getMessage();
}
return str;
Here more specification:
I want to take the data from the file in order to do that : JSONObject(jString).
when I extract the path of json file I create a file with the path and pass it to the function that reads from the file, and there it throws me a file not found exception, when I try to read from it.
The file does exists in the folder (even visually - I've tried to attach an image but the site won't let me, because I'm new user)
I've tried to open the file through the windows address bar by typing the path like that:
C:\Users\Marat\IdeaProjects\MyTask\JSON file\gallery.json and it opens it.
if you store it in the assets folder you can access it by using
InputStream is = context.getResources().getAssets().open("sample.json");
You can then convert it to a String
public static String inputStreamAsString(InputStream stream)
throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(stream));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
br.close();
return sb.toString();
}
EDIT
You need to put the file in the device, if it is on your computer, it is not accessible from your device. There are some ways to do that, and one of them is to put it in the res/ dir of your application. Please refer to the documentation to see how to do that.
Debug it. I'm pretty sure it will be very easy to find. To start with, look for the following:
Print the path before you create the file, e.g. Log.d("SomeTag", path + "/JSON file/gallery.json")
Observe the full exception details. Maybe there is another problem.
Explore the folders and see if the file exists (in eclipse: window -> show view -> other -> android -> file explorer.
You will probably observe the problem and be able to fix it. If not, post here a question with more details, including the results of those trials.
BTW, GetgetRootDirectory() returns the root directory of android, that's not what you want (you don't have RW permissions there) you probably want to get the applcation directory, you can see how to get it here, in the question I asked a few month ago. But since you didn't give us those details, it will be hard to help you more then that.