Access Unity StreamingAssets on Android
Solution 1
It may sound hard, but you really have to use the WWW
class to access your files from StreamingAssets
on Android.
And you have to keep a list of your filenames, to be able to access them, as there seems to be no way to use something like File.Exists
or Directory.GetFiles
.
Example:
WWW www = new WWW(source);
yield return www;
if (string.IsNullOrEmpty(www.error))
{
// make sure the destination directory exists
File.WriteAllBytes(destination, www.bytes);
}
else
{
// error handling
}
Your source files (base) path should look like this:
jar:file://" + Application.dataPath + "!/assets/
From the docs:
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.
Source: http://docs.unity3d.com/Manual/StreamingAssets.html
Solution 2
To solve this problem I created pre-compilation script which will run before the compilation; this script will list the names of the files you want to access later in text file
#if UNITY_EDITOR
using UnityEditor.Build;
using UnityEditor;
using System.IO;
public class BM_AndroidBuildPrepartion : IPreprocessBuild
{
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(BuildTarget target, string path)
{
// Do the preprocessing here
string[] fileEntries = Directory.GetFiles("Assets/Resources/Prefabs/alphabet", "*.prefab");
System.IO.Directory.CreateDirectory("Assets/StreamingAssets/");
using (StreamWriter sw = new StreamWriter("Assets/StreamingAssets/alphabet.txt", false))
{
foreach (string filename in fileEntries) {
sw.WriteLine(Path.GetFileNameWithoutExtension(filename));
}
}
}
}
#endif
then I read the text file and you can access the files in same way but you have to put them inside your project in Assets/StreamingAssets/
#if UNITY_ANDROID
string path = "jar:file://" + Application.dataPath + "!/assets/alphabet.txt";
WWW wwwfile = new WWW(path);
while (!wwwfile.isDone) { }
var filepath = string.Format("{0}/{1}", Application.persistentDataPath, "alphabet.t");
File.WriteAllBytes(filepath, wwwfile.bytes);
StreamReader wr = new StreamReader(filepath);
string line;
while ((line = wr.ReadLine()) != null)
{
//your code
}
#endif
Related videos on Youtube
Elbbard
Updated on June 04, 2022Comments
-
Elbbard almost 2 years
I wrote some code to get all the folders inside a folder inside the StreamingAssets and then to get all the files in that folder. It works well on Windows but I can't get it to work on Android.
Here is the code :
foreach (string s in Directory.GetDirectories(model.path + "/Levels")) { GameObject el = (GameObject)Instantiate(listButton, Vector3.zero, Quaternion.identity); el.transform.SetParent(grid1.transform); string[] words = s.Split('/'); #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN words = s.Split('\\'); #endif el.GetComponentInChildren<Text>().text = words[words.Length - 1]; el.GetComponent<Button>().onClick.AddListener(() => { MapSizeButtonClick(Int32.Parse(el.GetComponentInChildren<Text>().text)); }); }
I had to add the #if UNITY_STANDALONE_WIN for windows because of the '\'.
Then for the files inside that folder I wrote this :
foreach (string s in Directory.GetFiles(model.path + "/Levels/" + model.mapSize + "/" + model.colorNumber)) { string[] words = s.Split('/'); #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN words = s.Split('\\'); #endif words = words[words.Length - 1].Split('.'); if (words[1] == "json") { GameObject el = (GameObject)Instantiate(listButton, Vector3.zero, Quaternion.identity); el.transform.SetParent(grid3.transform); el.GetComponentInChildren<Text>().text = words[0]; el.GetComponent<Button>().onClick.AddListener(() => { levelButtonClick(Int32.Parse(el.GetComponentInChildren<Text>().text)); }); } }
I'm using UnityEngine.UI.Directory but I read that it can't be use on Android and I must use WWW instead. Something like this :
string xmlSetUpPath = "jar:file://" + Application.dataPath + "!/assets/xml/xmlSetUp.xml"; WWW book1WWW = new WWW(book1Path); yield return book1WWW; if(!File.Exists(Application.persistentDataPath + "/xml/book1.xml")) { File.WriteAllBytes(Application.persistentDataPath + "/xml/book1.xml", book1WWW.bytes); }
However I couldn't find anything like Directory.getFiles() or Directory.getFolder(). So I thought I could use WWW to check if the file exists (as the file names are 1.json, 2.json, 3.json) and iterate until a file doesn't exist.
But I would also need to check if a folder exists for the 1st part of the script and I can't find how to check if a folder exists with WWW. (My folders names are also 1,2,3,4...)
So how to check if a folder exists with WWW ?
Or what else can I do ?
UPDATE :
I updated the code, now it looks like this :
for (int i = 0; i < 100; i++) { if (Directory.Exists(Path.Combine(model.path, "Levels/" + i.ToString()))){ GameObject el = (GameObject)Instantiate(listButton, Vector3.zero, Quaternion.identity); el.transform.SetParent(grid1.transform); el.GetComponentInChildren<Text>().text = i.ToString(); el.GetComponent<Button>().onClick.AddListener(() => { MapSizeButtonClick(Int32.Parse(el.GetComponentInChildren<Text>().text)); }); } }
I use path.combine() and check every folder from 0 to 100. It works on Windows but it still doesn't work in Android.
The path is set like this :
#if UNITY_IPHONE path = Application.dataPath + "/Raw"; #endif #if UNITY_ANDROID path = "jar:file://" + Application.dataPath + "!/assets"; #endif #if UNITY_STANDALONE || UNITY_EDITOR path = Application.dataPath + "/StreamingAssets"; #endif
QUESTION UPDATE
I made a script that generate the file paths. The paths are always the same : "FolderNumerotedFromYToX/FolderNumerotedFromYToX/FileNumerotedFrom1ToX.json". The generation works but the writing to JSON doesn't. It outputs "{}".
// Generate level list List<KeyValuePair<int, List<KeyValuePair<int, List<int>>>>> l; l = new List<KeyValuePair<int, List<KeyValuePair<int, List<int>>>>>(); for (int i = 1; i < 100; i++) { if (Directory.Exists(Path.Combine(Path.Combine(Application.dataPath, "StreamingAssets"), "Levels/" + i.ToString()))) { l.Add(new KeyValuePair<int, List<KeyValuePair<int, List<int>>>>(i, new List<KeyValuePair<int, List<int>>>())); for (int j = 1; j < 100; j++) { if (Directory.Exists(Path.Combine(Path.Combine(Application.dataPath, "StreamingAssets"), "Levels/" + i + "/" + j))) { l[l.Count - 1].Value.Add(new KeyValuePair<int, List<int>>(j, new List<int>())); int k = 1; while (true) { if (File.Exists(Path.Combine(Path.Combine(Application.dataPath, "StreamingAssets"), "Levels/" + i + "/" + j + "/" + k + ".json"))) { l[l.Count - 1].Value[l[l.Count - 1].Value.Count - 1].Value.Add(k); } else break; k++; } } } } } string ljson = JsonUtility.ToJson(l); var lsr = File.CreateText("Assets/StreamingAssets/levels.json"); lsr.WriteLine(ljson); lsr.Close();
Can you help me finding a way to store this to a file ?
-
LorDFaKeR about 8 yearsIMO you should use Path.Combine() to concatenate a valid path to a string. And just where did you read that you can't use "normal" File operations? File.Exists() should just work fine.
-
Elbbard about 8 yearsI will try Path.Combine(), thanks ! I read it here forum.unity3d.com/threads/… and in other places, they say you must use WWW on Android. Maybe I got a little bit confused, File.Exists() should work but it's about Directory.GetFiles() and Directory.GetFolders().
-
Elbbard about 8 yearsI'm getting tired searching... (more than 4 hours now) I found another code where they use Application.streamingAssets. snip2code.com/Snippet/14142/… . Should I use this ?
-
-
Elbbard about 8 yearsI made a script which generate the list of the files (Added to the post). But now I don't know how to store it in a file. The JSON part doesn't work and juste writes "{}" to the file. Can you help me ?
-
d4Rk about 8 yearsAre you able to load/read the files on Android now? That was the actual question, wasn't it? But ok.. do you create the JSON stuff during runtime? If so, you should not put them in Streaming Assets.. Use Application.persistentDataPath for such data.