Johannes Dorn
Johannes Dorn

Reputation: 1337

Check if file is in (sub)directory

I would like to check whether an existing file is in a specific directory or a subdirectory of that.

I have two File objects.

File dir;
File file;

Both are guaranteed to exist. Let's assume

dir  = /tmp/dir 
file = /tmp/dir/subdir1/subdir2/file.txt

I want this check to return true

For now i am doing the check this way:

String canonicalDir = dir.getCanonicalPath() + File.separator;
boolean subdir = file.getCanonicalPath().startsWith(canonicalDir);

This seems to work with my limited tests, but i am unsure whether this might make problems on some operating systems. I also do not like that getCanonicalPath() can throw an IOException which i have to handle.

Is there a better way? Possibly in some library?

Thanks

Upvotes: 17

Views: 20942

Answers (9)

Rosberg Linhares
Rosberg Linhares

Reputation: 3687

Since Java 7+ you can just do this:

file.toPath().startsWith(dir.toPath());

Upvotes: 0

ManoDestra
ManoDestra

Reputation: 6473

You can do this, however it won't catch every use case e.g. dir = /somedir/../tmp/dir/etc..., unless that's how the file was defined also.

import java.nio.file.Path;
import java.nio.file.Paths;

public class FileTest {
    public static void main(final String... args) {
        final Path dir = Paths.get("/tmp/dir").toAbsolutePath();
        final Path file = Paths.get("/tmp/dir/subdir1/subdir2/file.txt").toAbsolutePath();
        System.out.println("Dir: " + dir);
        System.out.println("File: " + file);
        final boolean valid = file.startsWith(dir);
        System.out.println("Valid: " + valid);
    }
}

In order for the checks to work correctly, you really need to map these using toRealPath() or, in your example, getCanonicalPath(), but you then have to handle exceptions for these examples which is absolutely correct that you should do so.

Upvotes: 0

In addition to the asnwer from rocketboy, use getCanonicalPath() instad of getAbsolutePath() so \dir\dir2\..\file is converted to \dir\file:

    boolean areRelated = file.getCanonicalPath().contains(dir.getCanonicalPath() + File.separator);
    System.out.println(areRelated);

or

boolean areRelated = child.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator);

Do not forget to catch any Exception with try {...} catch {...}.

NOTE: You can use FileSystem.getSeparator() instead of File.separator. The 'correct' way of doing this will be to get the getCanonicalPath() of the directory that you are going to check against as a String, then check if ends with a File.separator and if not then add File.separator to the end of that String, to avoid double slashes. This way you skip future odd behaviours if Java decides to return directories with a slash in the end or if your directory string comes from somewhere else than Java.io.File.

NOTE2: Thanx to @david for pointing the File.separator problem.

Upvotes: 17

tsauerwein
tsauerwein

Reputation: 6041

This method looks pretty solid:

/**
 * Checks, whether the child directory is a subdirectory of the base 
 * directory.
 *
 * @param base the base directory.
 * @param child the suspected child directory.
 * @return true, if the child is a subdirectory of the base directory.
 * @throws IOException if an IOError occured during the test.
 */
public boolean isSubDirectory(File base, File child)
    throws IOException {
    base = base.getCanonicalFile();
    child = child.getCanonicalFile();

    File parentFile = child;
    while (parentFile != null) {
        if (base.equals(parentFile)) {
            return true;
        }
        parentFile = parentFile.getParentFile();
    }
    return false;
}

Source

It is similar to the solution by dacwe but doesn't use recursion (though that shouldn't make a big difference in this case).

Upvotes: 10

michal
michal

Reputation: 1806

You can traverse File Tree starting from your specific DIR. At Java 7, there is Files.walkFileTree method. You have only to write your own visitor to check if current node is searched file. More doc: http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#walkFileTree%28java.nio.file.Path,%20java.util.Set,%20int,%20java.nio.file.FileVisitor%29

Upvotes: 0

rocketboy
rocketboy

Reputation: 9741

How about comparing the paths?

    boolean areRelated = file.getAbsolutePath().contains(dir.getAbsolutePath());
    System.out.println(areRelated);

or

boolean areRelated = child.getAbsolutePath().startsWith(parent.getAbsolutePath())

Upvotes: -1

dacwe
dacwe

Reputation: 43504

I would create a small utility method:

public static boolean isInSubDirectory(File dir, File file) {

    if (file == null)
        return false;

    if (file.equals(dir))
        return true;

    return isInSubDirectory(dir, file.getParentFile());
}

Upvotes: 9

r3ap3r
r3ap3r

Reputation: 2815

public class Test {
public static void main(String[] args) {
    File root = new File("c:\\test");
    String fileName = "a.txt";
    try {
        boolean recursive = true;

        Collection files = FileUtils.listFiles(root, null, recursive);

        for (Iterator iterator = files.iterator(); iterator.hasNext();) {
            File file = (File) iterator.next();
            if (file.getName().equals(fileName))
                System.out.println(file.getAbsolutePath());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

Upvotes: 0

Luca Basso Ricci
Luca Basso Ricci

Reputation: 18403

If you plan to works with file and filenames heavly check apache fileutils and filenameutils libraries. Are full of useful (and portale if portability is mamdatory) functions

Upvotes: 1

Related Questions