File operations
Find, copy, move/rename and delete files with Java
Pre-filing
We’ll see some simple one line examples on operations on files themselves without modifying contents. We’ll just use standard Java classes. To run these commands make sure you have installed j’bang and then at your terminal do:
jbang -i https://raw.githubusercontent.com/shadowmanos/shadowmanos.github.io/main/content/tutorials/processingFiles/fileOperations.jsh
List folders and files
First step is always to create
a Path that represents a path to
a file or folder, and in many ways supersedes the
older File class. To create one from
scratch, pass a String
with the path. E.g. to make a Path
out of your home folder:
var home = Path.of(System.getProperty("user.home"))
The following will list names of all non-hidden files only in your home folder without going into subfolders (we are making use of StreamEx):
StreamEx.of(Files.walk(home, 1)).
map(Path::toFile).
filter(File::isFile).
remove(File::isHidden).
map(File::getName).
toList()
walk() will traverse (depth-first) the path going into folders up to the specified depth. 1 means only current folder.
To recursively search for files by name, try the following:
var someFile = StreamEx.of(Files.walk(home)).
map(Path::toFile).
filter(File::isFile).
filterBy(File::getName, "someText.txt").
findFirst().
get()
Note that with first()
you can get a new Path
representing a folder, and use it as a basis for future searches:
var pictures = StreamEx.of(Files.walk(home, 1)).
map(Path::toFile).
filter(File::isDirectory).
filterBy(File::getName, "Pictures").
findFirst().
map(File::toPath).
get()
You may list files in the “Pictures” folder (that we found above), sorted by when they were last modified, like this:
StreamEx.of(Files.walk(pictures, 1)).
map(Path::toFile).
filter(File::isFile).
sortedBy(File::lastModified).
toList()
We may want to filter files per the parent folder. For example, count Java source files ignoring files containing tests,
in a multi-project source code folder. These will be in subfolders of more than one folder named src/main/java
(
slashes may be the other way around in other operating systems).
StreamEx.of(Files.walk(home)).
map(Path::toFile).
filter(File::isFile).
filter(f -> f.getAbsolutePath().contains("src/main/java")).
filter(f -> f.getName().endsWith(".java")).
count()
Another useful op would be to find duplicates across different folders by name:
var fileCounts = StreamEx.of(Files.walk(home)).
map(Path::toFile).
filter(File::isFile).
groupingBy(File::getName, counting())
var duplicates = EntryStream.of(fileCounts).
filterValues(count -> count > 1).
keys().
toList()
Size matters
To get the size of a file in bytes:
someFile.length()
The above is not useful for folders. To get the size of all files in a folder we need to iterate over them:
StreamEx.of(Files.walk(pictures, 1)).
map(Path::toFile).
filter(File::isFile).
mapToLong(File::length).
sum()
From any file, you can get usage and size information for the whole disk:
someFile.getFreeSpace()
someFile.getUsableSpace()
someFile.getTotalSpace()
Expect usable space to be less than free space
Modify files
To create a new empty folder:
var someFolder = pictures.resolve("someFolder")
someFolder.toFile().mkdir()
To create a new empty file:
var someText = someFolder.resolve("someText.txt").toFile()
someText.createNewFile()
you can chain two resolve
calls, but you have to create a folder before creating a file within it.
We can rename and/or move a file by:
var movedFile = pictures.resolve("someFileMovedToDocs.txt").toFile()
someText.renameTo(movedFile)
This method takes a File
argument and since the file is under a different folder, it was moved as well as renamed. So
it’s similar to Unix mv command. If you now type someText
to inspect the variable,
you’ll see it still refers to the original path $HOME/Pictures/someFolder/someText.txt
that no longer exists.
Consequently, if you try to rename someText
again it will fail and return false
. We can re-create this file if we
want, in the original folder, with the same command as above.
If we wanted to copy instead, keeping the original file:
var copiedFile = Files.copy(someText.toPath(), someFolder.resolve("copyOfSomeText.txt")).toFile()
If you regret copying the file, to get rid of it:
copiedFile.delete()