- [Tim] All right, so now we know how to read directories;
we know how to use globs to retrieve paths
within a directory that match a given pattern,
and also how to write a filter that allows us
to retrieve paths based on attributes other than the name.
Now, before we learn how to walk a file tree
or a directory tree, let's tie up a few loose ends.
Firstly, the file separator.
Now, most of you probably know that on Windows,
you use the backslash to separate a path segment
or path segments, but on Unix and Linux systems,
you use the forward slash.
When building paths and/or writing regular expressions
or globs, you may need to know the file separator.
Now unless you're only writing applications for yourself,
as you're doing when you're learning,
you generally can assume that you're on a specific
operating system when you're writing Java code.
So there are actually two ways
to get the separator for the file system.
Now, firstly, you can use the file separator constant,
which is still commonly used, but another alternative
is to call the FileSystems.getDefault.getSeparator method.
So let's do that both ways and print out the results.
So outside of the try-loop,
we're going to do String separator
is equal to File.separator.
That's using the constant.
We can print that out, (separator), and we do separator
is equal to FileSystems.getDefault().getSeparator().
We can also print that out.
Let's just check that out.
Now, you can see, when I run this on a Mac,
I've got the forward slash, but if you were running this
on Windows, you should see the backslash,
and if you're running this on Linux,
you should see the forward slash as well,
because obviously Mac is based on Unix as well.
So the point here is that we've got the option
to retrieve the separator, and it's actually good practise
to never hard-code the file separator.
Now, we've been doing that so far
because we're not going to release our demo applications,
and if we had insisted on using the file separator
when testing, the code would have been pretty ugly to read.
Simplicity's obviously better when you're learning,
but in a real-world application,
you do the following to build the path,
which I'm about to type in, rather than hard-coding.
So let's have a go at doing that.
I'm talking about this code up here specifically
on line 20, 'cause you can see obviously there,
the file separator is hard-coded on line 20,
and will only work, currently,
for a Unix-derived operating system, Mac or Linux.
But we can actually change that, so let's do that.
I'm going to take a copy of that line,
and comment one out.
Let's see, we've got the old version there.
So what we're going to do is change this
to input FileTree, down here,
plus File.separator, plus,
and then I'll put the rest of the path we want,
do it that way.
Now, that way, we're now no longer hard-coding
the separator character.
So if we run this, the code still obviously works
on a Mac, but you'll find now that we've done this,
this would also work on Windows unmodified.
So that's obviously the way to go
when you're actually producing applications
and releasing them commercially.
Now, when you're building long paths,
instead of doing what I've done there,
you'd probably use a StreamBuilder,
but again, for the purposes of learning
and keeping the code readable, I'm going to continue
to hard-code the separator, but keep in mind
that that's how you would do it
in a real-world application to make sure
that you're supporting all operating systems.
All right, so the other thing we wanna do now
is figure out how to create temporary files.
Now, sometimes, an application needs to create files
that it doesn't want to keep.
Now, the files.createTempFile method creates
a temporary file in the operating system's
default temporary file directory,
so let's have a look at an example of doing that.
When I come down here and add another try block for it.
Try, and we'll catch an IOException.
And we'll just print e.getMessage if there is an exception.
The actual code, let's try Path tempFile is equal
to Files.createTempFile, and we'll call up myapp
as the first argument.
For the second argument, we'll put .appext.
Then we'll print out
Temporary File path is equal to
plus tempFile.toAbsolutePath.
All right, so the first parameter
in the createTempFile method is a prefix.
Now, the temporary file created will have a name
that begins with this prefix, and the second parameter,
you can see here, well, that's a suffix,
so in this case, you're passing the file extension,
which is usually what's passed for this parameter.
Now, you can actually pass
a third optional parameter of type file attribute
that specifies attributes for the file,
but for temporary files, there's often no reason to do that.
So we actually run this.
And you can see, in my case here,
I've got the temporary file path, being on a Mac,
is the /var, and we've got this subfolder,
/folders/az, and then this long filename,
which is still part of the path, and then /t,
but things there's this myapp, and that's that prefix
that I talked about that we specified here
in the part of the file, and then it's got
a temporary ID that's been added automatically by Java,
and then the file extension .appext.
And the location will actually be different depending
on the operating system you're running on, obviously.
In the case of Windows, you'll probably be
something along the lines of your C folder...
I should just type that out,
just go you get a rough idea.
It'll probably be something along this lines,
I would suspect.
So it's C:\Users.
Whatever your username is,
I'll replace that with the question marks,
and then we'll do something like AppData\Local\Temp,
and then it will probably be the filename,
'cause you can see here, something along those lines
will actually be appended.
So you'd probably see something like that on Windows,
where again, the three question marks
would be the username of the account you're using.
But you can see, that will be configured
and specified automatically.
The bottom line is you've got access to this file now,
which you can use.
One thing to note, though, is that the resulting path name
is always associated with the default file system.
Now, if you wanna store the temporary file somewhere else,
you can do that by using another version
of the createTempFile method that accepts more parameters,
including a parent directory path, and there are also
files.createTempDirectory methods as well,
if you wanna investigate those at your leisure as well.
Now, the other thing I want to talk about is filestores
or what you might be thinking of as drives.
Now, on a Unix system,
each mounted system is a filestore.
Now, on Windows, each drive or volume is a filestore.
For example, this C:.
The C drive, C:, or the D drive, D:.
So how do we get the filestores?
Well, we can actually use
the FileSystem.getFileStores method for that.
So let's go down and go over the code for that.
Let's try Iterable
stores is equal to
FileSystems.getDefault().getFileStores(),
and this code should work on all operating systems.
For(FileStore store : stores),
and we can print out store.name().
Now, the method returns an Iterable of FileStore.
This is the getFileStores.
And I haven't highlighted this,
but the FileSystems.getDefault method, well,
that actually returns a file system object.
Now, some systems can have more than one file system,
and when you call getDefault, you're getting the default
and often, only file system.
So if I actually run this.
I'm obviously get the Mac equivalent.
You can see here, I've got the various filestores
for my Mac, but when you run this on your system,
you'll get different results, depending on basically
the filestores that are on your system.
So it's basically system-specific, as I mentioned.
On Windows, you'll probably see the names
of the volumes printed out, not the drive letters.
Keep that in mind.
And it's probably more useful to get the drive letters,
and there's actually two ways to get them.
So let's have a look at doing that.
So I'm going to leave that code there,
but what we can also do is I'll just take a copy of that,
and actually what I'll do is I'll just leave that
in that loop, and we'll just also print it out here.
We'll print out System.out.println(), and we'll do store.
We'll just print out store.
Let's have a look at that.
You can see, in my case,
we've got there /Volumes/Production,
and then we've got dev/disk6,
so if you're on a Windows machine,
you'll probably see that that will be the driver
but that should actually be the drive letter,
actually showing for your machine.
So basically, the volume name and the drive letter.
So this one'll give you the volume name
and the drive letter.
That's that first option there.
We'll just clear...
Let's actually do that.
We'll put "Volume name/Drive letter =."
then we'll just put "file store =."
Let's just run that again,
so that's a little bit clearer.
Volume name/Drive letter.
In my case, I'm a Mac, and then also the filestore itself.
And what you could do is you can actually parse the string
to get the drive letter if you're on a Windows machine.
Potentially risky.
Some developers would consider that risky
in the case of the formatting of the return string,
if that ever changes, but the likelihood of that happening
is probably pretty low.
And obviously, that's only for Windows
that I'm talking about here.
If you're not on Windows,
you probably don't need to do any parsing
for the reasons which you can see on the screen there now.
You're not actually getting that drive letters,
'cause it's not really applicable for that operating system.
Now, there's another way of doing thing as well.
It's not ideal, as you'll see in a minute,
but let's actually go through and do that as well.
So we could come down here and do something like
Iterable rootPaths
is equal to FileSystems.getDefault
.getRootDirectories() for(Path path : rootPaths).
Then we could do, print out a path.
If we actually run that,
then perhaps it may be a bit more clearer.
That is the root directory, in the case of my machine.
So when you run this on a Windows machine,
only the drive letters should be printed,
but drive letters for all available drives
in this system will be printed
whether they're actually available or not.
For example, if you had four USB ports
but only two of them were occupied, drive letters
for all four would actually still be printed,
and again, it's not really very useful,
me showing you this on a Mac, because you can see here
we're really only got this one line,
which is a pointer to the root directory on my system.
But you'll get different results on a Windows machine.
The truth is, though, they usually don't need to get
the root drives, because if you think about it,
there aren't many cases, use cases, for this,
and that's because when an application's installed,
it remembers or can find out where it lives
on the file system, and if it then needs the user
to tell it where to save files or where to load them from,
the user will usually be presented with a dialogue,
and can then navigate to or type in the path.
So in other words,
you may never need to get the root directory
or directories, but if you do have to,
one of these methods that I've shown you
will actually work for your application.
Right, so let's finish the video here,
but in the next video, we're going to start
going through learning how to walk a file tree.
Now, when I'm talking about walking a file tree,
we mean that we're going to visit
every file and directory that can be reached
from a starting or root directory for the tree.
So I'll see you in the next video.