Java dir/files list

1.6k views Asked by At

I am trying to list all directories and files within a certain directory, but while "D:\data" works, "D:\" doesn't. "D" is a secondary disk.

This is the exception:

Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at java.util.Arrays$ArrayList.<init>(Arrays.java:3813)
    at java.util.Arrays.asList(Arrays.java:3800)
    at project.1.scavenger.listf(scavenger.java:19)
    at project.1.scavenger.listf(scavenger.java:30)
    at project.1.scavenger.listf(scavenger.java:30)
    at project.1.main(Project1.java:28)
Java Result: 1
BUILD SUCCESSFUL (total time: 0 seconds)

Code:

public static List<File> listf(String directoryName) throws IOException {
    File directory = new File(directoryName);
    List<File> resultList = new ArrayList<File>();

    // get all the files from a directory
    File[] fList = directory.listFiles();
    resultList.addAll(Arrays.asList(fList));
    for (File file : fList) {
        if (file.isFile()) {
            System.out.println(file.getAbsolutePath());
            try {
                System.out.println(scavenger.checkmime(file.getAbsolutePath()));
            } catch (Exception ex) {
            }
        } else if (file.isDirectory()) {
            resultList.addAll(listf(file.getAbsolutePath()));
        }
    }
    // System.out.println(fList);
    return resultList;
}

public static String checkmime(String fl) throws MalformedURLException, IOException {
    File file = new File(fl);
    String mimeType = file.toURL().openConnection().getContentType();
    // System.out.println(mimeType);
    return mimeType;
}

What's wrong with my code?

2

There are 2 answers

2
aw-think On BEST ANSWER

Removed error from your version

In your recursion you never ask for null values. Do it and it should run like this:

  public static List<File> listf(String directoryName) throws IOException {
    File directory = new File(directoryName);

    List<File> resultList = new ArrayList<>();

    // get all the files from a directory
    File[] fList = directory.listFiles();
    // this was missing
    if (fList == null) {
      return null;
    }
    resultList.addAll(Arrays.asList(fList));
    for (File file : fList) {
      if (file.isFile()) {
        System.out.println(file.getAbsolutePath());
        try {
          System.out.println(checkmime(file.getAbsolutePath()));
        } catch (Exception ex) {

        }
      } else if (file.isDirectory()) {
        // ask here if it was null
        List<File> files = listf(file.getAbsolutePath());
        if (files != null) {
          resultList.addAll(files);
        }
      }
    }
    //System.out.println(fList);
    return resultList;
  }

Why D:\data works and D:\ not

In every root of a drive in a Windows System is a hidden folder structure called $RECYCLE.BIN. In this folder windows stores for each user (sid) an own folder with deleted data (links to it). But a normal user is only allowed to get the first level and not the user folder (with sid value as name).

(German Windows: Papierkorb = Trash)

enter image description here

A maybe much better solution:

A maybe better way of doing such searchings in tree's is to create an Iterator over the directory tree (like a composite iterator). This solution also uses only Java NIO features, so the platform should be changeable (haven't tested!) to for ex. Linux.

DirectoryTreeIterator.java

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;

public class DirectoryTreeIterator implements Iterator<Path> {

  private final Deque<Iterator<Path>> deque = new ArrayDeque<>();

  public DirectoryTreeIterator(Iterator<Path> it) {
    deque.push(it);
  }

  @Override
  public boolean hasNext() {
    if (deque.isEmpty()) {
      return false;
    } else {
      Iterator<Path> it = deque.peek();
      if (!it.hasNext()) {
        deque.pop();
        return hasNext();
      } else {
        return true;
      }
    }
  }

  @Override
  public Path next() {
    if (hasNext()) {
      Iterator<Path> it = deque.peek();
      Path p = it.next();
      try {
        // here is the magic recursive on only filtered paths
        if (Files.isDirectory(p) && Files.isReadable(p) && !Files.isHidden(p)) {
          deque.push(Files.newDirectoryStream(p).iterator());
        }
      } catch (IOException ex) {
        throw new UncheckedIOException(ex);
      }
      return p;
    } else {
      return null;
    }
  }

}

Then you are able to use it like a charm:

FileLister.java

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;

public class FileLister {

  public static void main(String[] args) throws IOException {
    Path p = Paths.get("D:\\");

    Iterator<Path> it = Files.newDirectoryStream(p).iterator();
    DirectoryTreeIterator dti = new DirectoryTreeIterator(it);
    while (dti.hasNext()) {
      Path path = dti.next();
      if (!Files.isDirectory(path)) {
        String mime = Files.probeContentType(path);
        System.out.println("Mime of File "
                + path.getFileName() + " is: " + mime);
      }
    }
  }
}
0
Tom On

If you add this code right beneath the directory.listFiles() call:

if (fList == null) {
    System.out.println("Couldn't read files in directory: " + directoryName);
    return resultList;
}

then you will get a message like this:

Couldn't read files in directory: G:\$RECYCLE.BIN\Windows SID

This Windows SIDs is security identifier for a user on your local machine. So that means your current user has no permission to read this directory (it belongs to a different user) in the "recycle bin" directory, therefore listFiles() returns null instead of a String[].

You could keep the check and the message I've posted, or you can implement a different way to handle "unreadable" directories.

About why "D:\\" failed and "D:\\data" not:

  • "D:\\" contains the mentioned "$RECYCLE.BIN" directory with restricted access
  • "D:\\data" has no directory where your current user isn't allowed to access/read it, therefore listFiles() never returned null.