Android: How to detect a directory in the assets folder?
I'm retrieving files like this
String[] files = assetFiles.list("EngagiaDroid");
How can we know wheth开发者_如何学编程er it is a file or is a directory?
I want to loop through the directories in the assets folder then copy all of its contents.
I think a more general solution (in case you have subfolders etc.) would be something like this (based on the solution you linked to, I've added it there too):
...
copyFileOrDir("myrootdir");
...
private void copyFileOrDir(String path) {
AssetManager assetManager = this.getAssets();
String assets[] = null;
try {
assets = assetManager.list(path);
if (assets.length == 0) {
copyFile(path);
} else {
String fullPath = "/data/data/" + this.getPackageName() + "/" + path;
File dir = new File(fullPath);
if (!dir.exists())
dir.mkdir();
for (int i = 0; i < assets.length; ++i) {
copyFileOrDir(path + "/" + assets[i]);
}
}
} catch (IOException ex) {
Log.e("tag", "I/O Exception", ex);
}
}
private void copyFile(String filename) {
AssetManager assetManager = this.getAssets();
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(filename);
String newFileName = "/data/data/" + this.getPackageName() + "/" + filename;
out = new FileOutputStream(newFileName);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e("tag", e.getMessage());
}
}
You may use list method of AssetManager. Any directory in asset should have one file at least, empty directory will be ignored when building your application. So, to determine if some path is directory, use like this:
AssetManager manager = activity.getAssets();
try {
String[] files = manager.list(path);
if (files.length > 0) {
//directory
} else {
//file
}
} catch (Exception e) {
//not exists.
}
I've discovered this variant:
try {
AssetFileDescriptor desc = getAssets().openFd(path); // Always throws exception: for directories and for files
desc.close(); // Never executes
} catch (Exception e) {
exception_message = e.toString();
}
if (exception_message.endsWith(path)) { // Exception for directory and for file has different message
// Directory
} else {
// File
}
It's a more faster as .list()
The appalling truth is that despite being asked nearly 10 years ago, no simple, elegant, roundly applauded method of determining whether an element in the array returned by AssetManager.list() is a file or a directory has been offered by any answer to date.
So, for example, if an asset directory contains a thousand elements, then seemingly a thousand I/O operations are necessary to isolate the directories.
Nor, for any element, does any native method exist for obtaining its parent directory - vital for something complex like an assets Browser / Picker - where you could end up looking at some seriously ugly code.
boolean isAssetDirectory = !elementName.contains(".");
The lateral approach that worked for me was to assume that any element without a dot (.) in its name was a directory. If the assumption is later proved wrong it can be easily rectified.
Asset files generally exist because you put them there. Deploy naming conventions that distinguish between directories and files.
Another way relying on exceptions:
private void checkAssets(String path, AssetManager assetManager) {
String TAG = "CheckAssets";
String[] fileList;
String text = "";
if (assetManager != null) {
try {
fileList = assetManager.list(path);
} catch (IOException e) {
Log.e(TAG, "Invalid directory path " + path);
return;
}
} else {
fileList = new File(path).list();
}
if (fileList != null && fileList.length > 0) {
for (String pathInFolder : fileList) {
File absolutePath = new File(path, pathInFolder);
boolean isDirectory = true;
try {
if (assetManager.open(absolutePath.getPath()) != null) {
isDirectory = false;
}
} catch (IOException ioe) {
isDirectory = true;
}
text = absolutePath.getAbsolutePath() + (isDirectory ? " is Dir" : " is File");
Log.d(TAG, text);
if (isDirectory) {
checkAssets(absolutePath.getPath(), assetManager);
}
}
} else {
Log.e(TAG, "Invalid directory path " + path);
}
}
and then just call checkAssets("someFolder", getAssets()); or checkAssets("", getAssets()); if you want to check the root assets folder. But be aware that the root assets folder contains also other directories/files (Eg. webkit, images, etc.)
In your particular case, since you retrieved the files through list
, you already know that these names exist. This simplifies the problem a lot. You can simply use this:
public static boolean isAssetAFolder(AssetManager assetManager, String assetPath) throws IOException {
// Attempt opening as a file,
try {
InputStream inputStream = assetManager.open(assetPath); inputStream.close();
return false; // A file indeed.
} catch (FileNotFoundException e) {
// We already know this name exists. This is a folder.
return true;
}
}
On the other hand, if you need a generic solution to detect if a certain path both exists and is a folder, you can use this:
public static boolean isAssetAFolder(AssetManager assetManager, String assetPath) throws IOException {
// Attempt opening as a file,
try {
InputStream inputStream = assetManager.open(assetPath); inputStream.close();
return false; // A file indeed.
} catch (FileNotFoundException e) {
// This could be a folder, or this path doesn't exist at all. Further checking needed,
return assetPathExists(assetManager, assetPath);
}
}
// If you are checking a file name "icon.png" inside your assets folder, the assetPath should be "icon.png".
public static boolean assetPathExists(AssetManager assetManager, String assetPath) throws IOException {
// Assume that "" exists by default,
if (assetPath.isEmpty()) return true;
// Reject paths that point outside the assets folder,
if (assetPath.startsWith("..") || assetPath.startsWith("/")) return false;
// For other file/folder paths, we'll search the parent folder,
File fileOrFolder = new File(assetPath);
String parent = ((parent=fileOrFolder.getParent()) != null) ? parent : ""; // Handle null parents.
if (!Arrays.asList(assetManager.list(parent)).contains(fileOrFolder.getName())) return false;
// Getting this far means that the specified assetPath indeed exists. However, we didn't handle files
// with trailing "/". For instance, "icon.png/" shouldn't be considered existing although "icon.png"
// does.
// If the path doesn't end with a "/", we are safe,
if (!assetPath.endsWith("/")) return true;
// Remove the trailing slash,
assetPath = assetPath.substring(0, assetPath.length()-1);
// Attempt opening as a file,
try {
InputStream inputStream = assetManager.open(assetPath); inputStream.close();
return false; // It's indeed a file (like "icon.png"). "icon.png/" shouldn't exist.
} catch (FileNotFoundException e) {
return true; // This is a folder that exists.
}
}
I wrote these for a web server, so I couldn't make assumptions about the shape of the input path. But it can be simplified a bit if you have some rules set. This code returns immediately once it becomes certain of the type of the asset, to avoid the extra processing overhead.
You can also try this, it works for me, since you cannot rely solely on .list()
public static boolean isDirectory(Context context, String path) throws IOException {
//If list returns any entries, than the path is a directory
String[] files = context.getAssets().list(path);
if (files != null && files.length > 0) {
return true;
} else {
try {
//If we can open a stream then the path leads to a file
context.getAssets().open(path);
return false;
} catch (Exception ex) {
//.open() throws exception if it's a directory that you're passing as a parameter
return true;
}
}
}
You can start from Android File
You can check if a File represents a directory using http://developer.android.com/reference/java/io/File.html#isDirectory(). Is that what you mean?
精彩评论