Reputation:
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?
Upvotes: 1
Views: 1680
Reputation: 4803
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;
}
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)
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.
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:
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);
}
}
}
}
Upvotes: 1
Reputation: 17567
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
.Upvotes: 1