1

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?

Tom
  • 16,842
  • 17
  • 45
  • 54
  • Which line is this: `at project.1.scavenger.listf(scavenger.java:19)`? – assylias Jun 20 '15 at 08:18
  • File#listFiles will return null when the abstract path doesn't exist or can't be read (ie a shortcut) – MadProgrammer Jun 20 '15 at 08:18
  • What OS are you using? – MadProgrammer Jun 20 '15 at 08:21
  • 1
    Do you write for the dir "D:\" or "D:\\"? Backslashes should be escaped. – aw-think Jun 20 '15 at 08:21
  • 1
    @MadProgrammer I guess Mac OS X, because Mac OS X is the only OS which uses drive letters, right? :P – Tom Jun 20 '15 at 08:21
  • And by the way, have a look here, if you want a faster solution: http://stackoverflow.com/questions/30920479/why-does-the-java-directorystream-perform-so-slow/30920911?noredirect=1#comment49882496_30920911 Use it with [probeContent](http://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#probeContentType-java.nio.file.Path-) – aw-think Jun 20 '15 at 08:24
  • 1
    @tom There is a difference in security models between windows 7 and windows 8 which may account for the inability to list the root drive – MadProgrammer Jun 20 '15 at 09:03
  • @MadProgrammer Oh, yes, that might be true. Your question sounds more like "do you use Linux, Mac or Windows" instead of "Which version of Windows" (at least to me). Then, sorry for that comment :). – Tom Jun 20 '15 at 09:05
  • 1
    @tom If they were using Unix, that would have at least explained something :p – MadProgrammer Jun 20 '15 at 09:11

2 Answers2

1

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);
      }
    }
  }
}
Community
  • 1
  • 1
aw-think
  • 4,723
  • 2
  • 21
  • 42
1

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.
Tom
  • 16,842
  • 17
  • 45
  • 54