I would like to list files contained into assets subdirectory called "subDir" and am using following code. However, if I set dirFrom = "" (empty), lists all folders in assets properly. However, for dirFrom = "/subDir/", doesn't work. I already tried "subDir/" and same result. Is necessary to declare permissions in manifest? Thank you.
private void copyFiles(String dirFrom, String dirTo) throws IOException {
AssetManager am = getAssets();
String fileList[] = am.list(dirFrom);
if (fileList != null)
{
for ( int i = 0;i<fileList.length;i++)
{
Log.d("",fileList[i]);
}
}
}
AssetManager.list(String) takes a "relative path within the assets".
Android / Java usually expects paths to have no '/' in the end.
Also "relative" means that there should be no '/' at the start - it could otherwise be a path in the root of your filesystem.
Using just "subDir" or "subDir/subsubDir" will work.
zapl´s answer is true, but additional info: If You want to copy the files, for example to sd card, You have to declare Your InputStream with a slash:
InputStream in = assetManager.open("subDir/"+filename);
This was something I have stumbled by copy files from a subfolder. My originally plan was to copy files directly from asset folder, this worked but gave me some IOExceptions. There were some directories "webkit","sound","images" and another one I can´t remember, inside the asset folder...but I have not seen them. This must be something Android internal that I don´t understand. But to get rid of this, I placed my files inside a subfolder. I know it is an old thread, but I put this information to this thread only to help others with similar problems.
Related
So I have a project, and this is one of the demands:
You should have a class named Project3, containing a main method.
This program reads the levels information from a file whose name is
specified as a command-line parameter (The file should also be
relative to the class-path as described here:)
All the file names specified in the levels and block definition files
should be relative to the class path. The reason we want them to be
relative to the class path is that later we will be able to read the
files from inside a jar, something we can not do with regular File
references.
To get an input stream relative to the class path (even if it's inside
a jar), use the following:
InputStream is =
ClassLoader.getSystemClassLoader().getResourceAsStream("image.png");
The idea is to keep a folder with files(definitions and images) and
then add that folder to the class path when running the JVM:
java -cp bin:resources ... If you don't add the resources folder to
you class path you wont be able to load them with the command from
above.
When run without parameters, your program should read a default level
file, and run the game accordingly. The location of the default level
file should be hard-coded in your code, and be relative to the
classpath_.
When run without parameters, your program should read a default level file, and run the game accordingly. The location of the default level file should be hard-coded in your code, and be relative to the classpath_.
The part of the code that handles the input is:
public Void run() throws IOException {
LevelReader level = new LevelReader();
List<level> chosenLevels = new ArrayList<>();
if (args.length >= 1) {
File f = new File(args[0]);
if (f.exists()) {
chosenLevels = level.makeLevel(args[0]);
}
}
if (chosenLevels.size() == 0) {
game.runLevels(defaultLevels);
} else {
game.runLevels(chosenLevels);
}
return null;
}
So my question is:
An argument should be the full path of a file which means:
D:\desktop\level3.txt
Is it possible to read a file from every location on my computer?
Because right now I can do it only if my text file is in the
project's directory (not even in the src folder).
I can't understand the rest of their demands. What does is mean "should be hard-coded in your code, and be relative to the
classpath_." and why is it related to InputStream method(?)
I'm confused all over this.
Thanks.
A classpath resource is not the same as a file.
As you have correctly stated, the full path of a file is something like D:\desktop\level3.txt.
But if ever want to distribute your application so it can run on other computers, which probably won’t have that file in that location, you have two choices:
Ask the user to tell the program where to find the file on their computer.
Bundle the file with the compiled program.
If you place a non-.class file in the same place as .class files, it’s considered a resource. Since you don’t know at runtime where your program’s class files are located,¹ you use the getResource or getResourceAsStream method, which is specifically designed to look in the classpath.
The getResource* methods have the additional benefit that they will work both when you are developing, and when the program is packaged as a .jar file. Individual entries in a .jar file are not separate files and cannot be read using the File or FileInputStream classes.
If I understand your assignment correctly, the default level file should be an application resource, and the name of that resource is what should be hard-coded in your program. Something like:
InputStream is;
if (args.length > 0) {
is = new BufferedInputStream(
new FileInputStream(args[0]));
} else {
// No argument provided, so use program's default level data.
is = ClassLoader.getSystemClassLoader().getResourceAsStream("defaultlevel.txt");
}
chosenLevels = level.makeLevel(is);
¹ You may find some pages that claim you can determine the location of a running program’s code using getProtectionDomain().getCodeSource(), but getCodeSource() may return null, depending on the JVM and ClassLoader implementation, so this is not reliable.
To answer your first question, it doesn't seem like they're asking you to read from anywhere on disk, just from within your class path. So that seems fine.
The second question, "What does is mean 'should be hard-coded in your code, and be relative to the classpath'?". You are always going to have a default level file in your project directory. Define the path to this file as a String in your program and that requirement will be satisfied. It's related to the InputStream because the stream requires a location to read in from.
My resources folder inside my jar includes a directory with several binary files. I am attempting to use this code to extract them:
try(InputStream is = ExternalHTMLThumbnail.class.getResourceAsStream("/wkhtmltoimage")) {
Files.copy(is, Paths.get("/home/dan/wkhtmltoimage");
}
This is throwing the error
java.nio.file.NoSuchFileException: /home/dan/wkhtmltoimage
Which comes from
if (errno() == UnixConstants.ENOENT)
return new NoSuchFileException(file, other, null);
in UnixException.java. Even though in Files.java the correct options are passed:
ostream = newOutputStream(target, StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE);
from Files.copy. Of course there's not! That's why I'm trying to make it. I don't yet understand Path and Files enough to do this right. What's the best way to extract the directory and all its contents?
Confused because the docs for Files.copy claims
By default, the copy fails if the target file already exists or is a symbolic link
(Apparently it fails if the target file doesn't exist as well?)
And lists the possible exceptions, and NoSuchFileException is not one of them.
If you're using Guava:
URL url = Resources.getResource(ExternalHTMLThumbnail.class, "wkhtmltoimage");
byte[] bytes = Resources.toByteArray(url);
Files.write(bytes, new File("/my/path/myFile"));
You could of course just chain that all into one line; I declared the variables to make it more readable.
The file that does not exist may actually be the directory you're trying to create the file in.
/home/dan/wkhtmltoimage
Does /home/dan exist? Probably not if you're on a Mac.
This question has been brought up before, and I have searched many of the answers. It always ends in "You want getResourceAsStream". This is not what I am looking for.
My issue is , for a game object, I am using a folder structure to keep sprite strips rather than having one large sprite. This results in :
Media/
CharacterName/
AnimationName/
image.extension
the programming object just holds it's folder as a string, and I pass the getResource() URL to an object to fill the map of images. there can be {n} number of AnimationName/ sub directories. My error comes from this code:
dir = new File(s.toURI());
I take the directory, and call listFiles and pass the file names found to the sprite loader. Here is a code snippet:
dir = new File(s.toURI());
File[] chld = dir.listFiles();
//get a list of files in the image/character folder
for(File f:chld)
{
//get a list of the files for each dir
File[] grandChild = f.listFiles();
for(File t:grandChild)
{
String fname = t.getAbsolutePath();
System.out.println(fname);
String temp = fname;
temp = temp.substring(temp.lastIndexOf("/") + 1,temp.lastIndexOf("."));
String animName = temp.replaceAll("\\d*$", "");
int numPics = 0;
Pattern p = Pattern.compile("[0-9]+");
Matcher m = p.matcher(temp);
while(m.find()){
numPics = Integer.parseInt(m.group());
}
System.out.println("animation name: " + animName);
System.out.println("file name: " + fname);
System.out.println("number of pictures: " + numPics);
Animations.put(animName, sl.loadStripImageArray(fname, numPics));
}
}
Excuse the poor naming and temp variables, it's still being worked on.
sl is the sprite loader, and Animations is a hash map. This works fine until I package the project. I don't want to write a bunch of convoluted code that only works if I have a jar file, and not when I'm working in netbeans with the source folders.
I have considered having an application data folder, but I'd like to stay with a jar package if I can.
You do still want to use getResourceAsStream. Nothing here requires that all resources must be kept at the same folder within the JAR. You can use relative paths, or absolute paths to the root of the JAR by prefixing your path with /.
You can't make File work with resources within the JAR - even if instantiated with a URL that points to a resource contained within a JAR.
You may have to rework some other things, as the classpath is not really meant to be enumerated against (as you're currently listing files from the parent directory). It is designed to retrieve a resource by name. So one possibility (that I would recommend) is to have a "manifest" file that contains the files you want to load from each directory. (Read this file, then load the additional resources by name.)
Alternatively, if you can find the name of the JAR file you're loading from, you can create a Jarfile from it, then call its entries() method to find all of the contained resources. But even then, they aren't returned in a "tree structure", so ideally, you'd read this one, create your own tree structure from it (possibly as a series of Maps), then use it to retrieve the "directory listings" as needed.
If you are absolutely sure that the sprites are located in a jar - you could try using the JarFile class. There is a method entries. I didn't try it but it seems to return all resources located in the whole jar file. You would have to filter out which resources are in the right path.
Android seems to make life pretty easy for loading resources of certain types. Once I leave the beaten path a little bit, it seems less helpful. I can load in images, sounds and other objects from the assets folder if and only if they are of certain types. If I try to load a binary file of my own format, I get a less than helpful 'file not found' exception, even though listing the directory shows that it is clearly there.
I've tried using the following methods to read a binary file of a custom format from the assets directory:
File jfile = new File("file://android_asset/"+filename); //tried to get the URI of the assets folder
JarFile file = new JarFile("assets/"+filename); //tried assuming the assets folder is root
fd = am.openNonAssetFd( filename); //tried getting my file as an non asset in the assets folder (n.b. it is definitely there)
fs = am.open(filename, AssetManager.ACCESS_BUFFER); //tried loading it as an asset
I'm thinking that there's something fundamental about android file I/O that I don't understand yet. The documentation for asset management seems incomplete and there must be some reason for deliberately making this unintuitive (something to do with security?). So, what's the fool proof, canonical way of loading a binary file of my own format within an android app?
UPDATE:
I tried file:///android_asset/ but still no joy.
String fullfilename = "file:///android_asset/"+filename;
File jfile = new File(fullfilename);
if (jfile.exists())
{
return new FileInputStream(jfile);
}
else
{
return null; //the file does exist but it always says it doesn't.
}
Are there any permissions for the file or in the project manifest that I need?
Thanks
I think the best way to load a file from the Assets folder would be to use AssetManager.open(String filename) - this gives you back an InputStream which you can then wrap in a BufferedInputStream and otherwise call read() to get the bytes. This would work regardless of the file type. What kind of problems have you had with this approach specifically?
I think you have left out the slash as in
File jfile = new File("file:///android_asset/"+filename);
There's three forward slashes, not two. :)
For me the solution was to uninistall the application, clean the project in Eclipse and run it again. The problem was Android couldn't find the new files I put in the asset folder.
I ended up reading this question so I hope this can be helpful to someone else.
My application has an assets directory in which I've dumped a bunch of text files I need to load at runtime.
I have a directory full of assets of a particular type (i.e., "assets/subdir") and I want to load all of the files in this directory, one at a time.
I have code like this:
AssetManager assetMgr = getAssets();
String[] assetsIWant = assetMgr.list("subdir");
for(String asset: assetsIWant) {
doAssetyThing(asset);
}
I've tried a zillion different versions of the parameter to assetMgr.list() and am not getting anywhere.
If I use "/", I get back a list containing the "assets" directory, and a few random other items (META_INF, for example). If I pass any other string (like "assets" or "assets/" or "/assets" or "/assets/" or "mysubdir" or "/mysubdir" or "assets/mysubdir" or ...) then I get back an empty array.
The documentation is unfortunately fairly incoherent.
Does anybody know what the correct formula for that list() parameter is?
Passing an empty string seems to work for me. I get a list of the files in my assets directory when I do the following:
AssetManager aMan = appContext.getAssets();
String[] filelist = aMan.list("");
I ve use the following code to list the file name in assets/myFolder/:
String[] fileNames =getAssets().list("myFolder");
for(String name:fileNames){
System.out.println(name);
}
note that the parameter in the method list does not contains "/".
When you need to have access to a folder down deeper in your hierarchy use
String[] fileNames =getAssets().list("myFolder"+File.separator+"mysubfolder");
instead of "/" inside the String, which would give you an empty String array as result.
I'm not sure why it works this way, but when I list "/", I get root level stuff, not things from my "assets" directory. I actually found no way to properly list my assets folder in the short time I spent trying.
The workaround I'm using is to create a "subdir" directory inside of my "assets" directory. I put all the assets that I want to access in this "subdir", and do:
String[] assetsIWant = assetMgr.list("subdir");
I don't know why "/" lists the "assets" directory itself as one of it's files, yet you don't have to specify the "assets" directory when giving list a path. Maybe someone else knows, but I hope this tip will help you out anyway.
There are a lot of similar questions around this topic, I suspect you are using the assets folder in a library project. if you try to access assets in a library project on android you will get empty contents and the exact symptoms you have described here
from the android "projects" documentation
source : http://developer.android.com/tools/projects/index.html
Library projects cannot include raw assets
The tools do not support the use of raw asset files (saved in the assets/ directory) in
a library project. Any asset resources used by an application must be
stored in the assets/ directory of the application project itself.
However, resource files saved in the res/ directory are supported.
if this is the case, the answer is that you should only include Raw assets in the assets folder of your application project NOT in any project marked as a library (you can check this by right clicking your project and selecting properties, select the android entry on the left and look at the is library checkbox)
Let's say you have this folder set up: Assets->SubDir1. You have things in Subdir1 like A.txt, B.txt, C.txt. So from the Project's directory, it would be Assets/SubDir1/A.txt.
Taking that into account, the way to access that file is similar to what you're doing, adding one thing. That one thing is in your doAssetyThing function. You have to put the SubDir1 directory in front of it, even if you did a filter of "SubDir1" in the .list("");
Example:
AssetManager am = getResources().getAssets();
String [] list = am.list("SubDir1");
for (String s:list) {
doAssetyThing("SubDir1/" + s);
//I use this next line as the start of copying a pdf from one location
//to another. I wanted to give you a real function in use.
InputStream inStream = am.open("SubDir1/" + s);
//more code
}
I hope this is clear and helps you out!
I use the following two methods to ensure given asset is present:
protected boolean hasAsset(String id) {
return hasAsset(id,"");
}
protected boolean hasAsset(String id, String dir) {
int idx = id.indexOf('/');
if (idx > 0 && idx < id.length() - 1) {
if (dir.length() > 0) {
dir = dir + "/" + id.substring(0,idx);
} else
dir = id.substring(0,idx);
return hasAsset(id.substring(idx + 1), dir);
}
try {
return Arrays.asList(context.getAssets().list(dir)).contains(id);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
For some reason, empty directories are not listed.
At least, in my experience, they are not listed.
That is, if you have some-dir/d1/f1 and some-dir/d2 in the assets area, and you list("some-dir"), d2 is not listed.
This solution seems working for me:
Keep a copy of each file you want to access from a sub directory in the assets directory too.
Use same convention. i.e. list("subdirectory")
It doesn't read the files in assets folder, but reads all "copied" files in its sub directory.
Weird!! But works!