- [Tim] All right, so let's now code the copy case.
What we're going to try and do here is copy everything
in the Dir2 folder to a Dir2 directory
that's within the Dir4 directory.
So to do this, we wanna override
the VisitFile method, the visitFileFailed method
and the preVisitDirectory methods.
Now the visitFileFailed method will be the same
as it is for the print example.
Alright, so let's start creating a new class for this.
We'll call this one CopyFiles.
Okay, we're going to extend simple file visitor again,
so extends SimpleFileVisitor=Path, accept that import.
Alright we start with the visitFileFailed,
and for that one we're just going to pint out
Error accessing file plus file.toAbsolutePath
plus a space plus e.getMessage.
And then we'll return continue which will be
FileVisitResult.CONTINUE.
Alright, so that's our visitFileFailed method,
nothing new there, we now add the preVisitDirectory method,
or override that, so let's do that.
preVisitDirectory, now in order to figure out the path
for the CopyFile, we're going to use path.reletivize method,
now this method constructs a relative path
that resolves to a given path, for example
with a path to relativize against is c:\path1
and the given path is c:\path1\path2\path3,
then the relativized path would be \path2\path3
which is the given path relative to c:\path1
if that makes sense.
So once we've got the relative path,
we then need to resolve it against
the copied directories location by calling
the path.resolve method and that will turn
the relative path we got from the relativize method
into the full path for the copied file.
Now what I said there may have sounded like gobbledygook,
so let's actually look at a concrete example.
Now one of the files that's gonna be copied
is located over here in the FileTree,
Dir2, in the Dir3 subfolder, it's file1.txt.
Now this file is gonna be copied into the FileTree,
into the Dir4 folder and it's gonna be a subfolder
called Dir2Copy then we're going to have
a subfolder of that called Dir3
and then that will be File1.txt,
so that automatically is gonna be the destination path.
So the root of the source path is the FileTree, Dir2,
that's where we're starting and the root
of the destination path is going to be
the FileTree, Dir4 and it's going to be the Dir2Copy folder
which at the moment doesn't exist.
So the relative path of the file in both
the source and destination roots will be Dir3,
the subfolder, subdirectory \file1.txt.
So you call the relativized method to get
the relative path to the source root.
And you can see I've got an example on the screen,
they're now for Windows to how show you the extra paths,
the various paths, now when doing a copy,
this would also be equal to the relative path
in the destination root, so you then call the result method
to get the full path of a copy location.
So basically the steps as you can see on the screen
as follows is the sourcePath as you can see there
FileTree\Dir2\Dir3\file1.txt and you can see
the sourceRoot then is going to be the FileTree\Dir2,
the targetRoot which we've talked about will be
FileTree\Dir4\Dir2Copy and Dir2Copy will be a new folder,
the relativizedPath will be
sourceRoot.relativize(sourcePath) so that will be
the call to actually get that path,
so that will give us Dir3\file1.txt
and the resolvedPathforCopy is gonna be
the targetRoot.resolve(relativizedPath)
and you can see there the path
is gonna be FileTree\Dir4\Dir2Copy\Dir3\file1.txt.
So hopefully that's clearer now, but we're essentially
figuring out the relative path to the sourceRoot
and then appending it to the targetRoot,
because when doing a copy the relative path
to both the source and target roots will be the same.
You actually may have noticed you need the sourceRoot path
and destination root path to figure out the path
for the copy, but the preVisitDirectory method
and the VisitFile methods are only passed
a reference to the directory or file that's being visited.
So you have to save the source and destination roots
as instance variables in the CopyFiles class
so consequently we're gonna add those fields
in the Constructor to handle that.
Alright, so we want a firstly private Path
that's gonna be sourceRoot and private Path targetRoot.
Let's create a Constructor and the constructor's
going to accept both the source and the target
and assign the fields or assign the parameters I should say
to the Constructor, to the appropriate fields.
So now that we've done that, we can the
preVisitDirectory method to get this working
and that will include some printlns in there as well.
So we started work on the preVisitDirectory
or we overrode it at least, but let's actually
write the code now.
We're gonna start by typing Path
that's relativizedPath = sourceRoot.relativize,
that's gonna be dir, then we're gonna do,
we're just gonna print out for that
so we can see what's happening,
so RelativizedPath = plus relativizedPath.
Next we wanna do the copyDirectory,
so it's path copyDir = targetRoot.resolve
and it's gonna be relativizedPath
and let's just print that out as well,
so it's gonna be Resolved path for copy =
plus copyDirectory or copyDir,
let's add a try to this now and we're gonna do
Files.copydir and then copyDir
and we have to catch an IOException if there is one
and the code of that we're just gonna print out the message
e.getMessage for the exception and then we'll return
if that does happen SKIP_SUBTREE.
Assuming we get past that, we're gonna return continue,
which will be FileVisitResult.CONTINUE.
Alright, so you get the relative path or we're getting
the relative path to the sourceRoot using the
relativized or relativize method here at line 32.
Then we're calling the result method to get the path
for the copy, this is on line 34, after that
we call in the copy method to make the copy, line 38,
and if an exception occurs as you saw,
we're gonna skip the subtree by returning
FileVisitResult.SKIP_SUBTREE and that means
that you stop processing entries for that directory.
Alright so that's the preVisitDirectory method,
overridden and working, we'll hope it'll work
when we test it, let's now override the visitFile method.
So for this one, just as with directories,
we're gonna use the relativize and result methods
to figure out the path for the copied file.
And once we've go the resolved destinationPath
we can call the copy method to do the actual copy.
And it's probably just as quick if I just delete that
and copy this code from the preVisitDirectory
and just change a few things rather than
just typing all that again, so let's go ahead and do that.
We do need to change a few things as I mentioned here.
For starters, we're gonna change instead of
this being directory, we'll make that file,
this will be file as well.
In the case of an exception what we're going to do
is just print out the exception,
we're not gonna return anything at that point.
But we will return a continue down here
once the method is finished.
So very similar as I mentioned to the preVisitDirectory,
we're using the relativize method and the result methods
to figure out the path for the copied file
and once we've got the result destination path,
we call in the copy method, you can there on line 55
to do the actual copying and fluff.
Alright so at this point now, we need to kick off the copy
in the same way we did when we printed the FileTree
in the previous video, so let's go back to the main method.
And we come down here and let's print out a message first.
And we'll go Copy Dir2 to Dir4/Dir2Copy,
so we know what's going on.
And Path copyPath = FileSystems.getDefault().getPath
and the path is gonna be FileTree plus File.separator
we'll do that so we can do this across platform
plus it's gonna be Dir4 plus
and we'll do the separator again,
File.separator plus and the folder name is gonna be,
or the directory name Dir2Copy.
So basically the path that we're gonna be copying to
is going to be FileTree/Dir4/Dir2Copy
and obviously backslashes if you're on windows.
But by using the File.separator we can
get this working for both platforms.
Alright, so that's the path that we're gonna be copying to
so let's actually invoke some code, so what we'll try here
and we'll put a catch in there for any IOExceptions
that might be raised or thrown
and we'll print out e.getMessage
if we find there is an exception,
then the actual code itself is gonna be
Files.walkFileTree(dir2Path) and dir2Path
is still assigned from line 57 which is our source folder
and then we're going to do new CopyFiles
and then we're going to do dir2Path again,
which is our source and the destination which is copyPath.
And that should be it.
Okay looking at Dir4 at the moment, there is no Dir2Copy.
So let's actually try running this and see if it works.
Okay so you can see that the Dir2Copy directory has appeared
if you open that up and have a look you can see
there's a Dir3 folder in the Dir2Copy, we open that up
there's file1 and file2 and also file1, 2 and 3 as well
still sitting there in the Dir subfolder,
we can close that down and see that
that seems to work quite nicely.
So looking back at our path, we had Dir2,
we had a Dir3 folder, we had file1.txt, file2,txt,
file3.dat that was in the Dir2 folder,
then in the Dir3 subfolder we had file1 and file2.txt
so we can see we've got a mirror copy
or mirror image of that in our Dir4\Dir2Copy folder
and the Dir3 folder has also got those two files.
So that's pretty cool, we've been able to actually copy
that entire tree now, now in a real world application
you'd probably want to beef up the code
to handle situations for example
like the destination file or directory already existing.
What you could do perhaps is prompt the user
and ask whether the existing file should be overwritten,
for directories you'd then have to decide if it was an error
when the directory you're copying to already exists.
In addition you can pass more parameters
to the copy method to specify what should happen
if the destination path already exists.
So you have to consider all these scenarios
in a real world app.
Alright so you've learned quite a lot about using
Java.NIO with the file system.
Alright, so let's finish the video here,
in the next video we're gonna actually start talking
a little bit about mapping common Java.IO operations
to Java.NIO, see you in the next video.