I have spent hours and hours searching for the answer and I just can't figure it out, I am trying to copy my resources folder which contains all the images and data files for my game I am working on out of the running jar and into
E:/Program Files/mtd/ It works fine when I run it out of eclipse, but when I export the jar and try it, I get NoSuchFileException
`JAR
Installing...
file:///C:/Users/Cam/Desktop/mtd.jar/resources to file:///E:/Program%20Files/mtd
/resources
java.nio.file.NoSuchFileException: C:\Users\Cam\Desktop\mtd.jar\resources
at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Sou
rce)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Sou
rce)
at sun.nio.fs.WindowsFileSystemProvider.readAttributes(Unknown Source)
at java.nio.file.Files.readAttributes(Unknown Source)
at java.nio.file.FileTreeWalker.walk(Unknown Source)
at java.nio.file.FileTreeWalker.walk(Unknown Source)
at java.nio.file.Files.walkFileTree(Unknown Source)
at java.nio.file.Files.walkFileTree(Unknown Source)
at me.Zacx.mtd.main.Game.<init>(Game.java:94)
at me.Zacx.mtd.main.Game.main(Game.java:301)`
This is the code I am using:
if (!pfFolder.exists()) {
pfFolder.mkdir();
try {
URL url = getClass().getResource("/resources/");
URI uri = null;
if (url.getProtocol().equals("jar")) {
System.out.println("JAR");
JarURLConnection connect = (JarURLConnection) url.openConnection();
uri = new URI(connect.getJarFileURL().toURI().toString() + "/resources/");
} else if (url.getProtocol().equals("file")) {
System.out.println("FILE");
uri = url.toURI();
}
final Path src = Paths.get(uri);
final Path tar = Paths.get(System.getenv("ProgramFiles") + "/mtd/resources/");
System.out.println("Installing...");
System.out.println(src.toUri() + " to " + tar.toUri());
Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) throws IOException {
return copy(file);
}
public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs ) throws IOException {
return copy(dir);
}
private FileVisitResult copy( Path fileOrDir ) throws IOException {
System.out.println("Copying " + fileOrDir.toUri() + " to " + tar.resolve( src.relativize( fileOrDir ) ).toUri());
Files.copy( fileOrDir, tar.resolve( src.relativize( fileOrDir ) ) );
return FileVisitResult.CONTINUE;
}
});
System.out.println("Done!");
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
This was harder that I thought, but here is how to do it.
here is my copy method Reference https://examples.javacodegeeks.com/core-java/io/file/4-ways-to-copy-file-in-java/
public void copyFile(String inputPath, String outputPath ) throws IOException
{
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = getClass().getResourceAsStream(inputPath);
outputStream = new FileOutputStream(outputPath);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
}
finally {
inputStream.close();
outputStream.close();
}
Please note the structure of the project of the Jar file in this image Project structure
Now I need to read the Jar file. This is a varition on this solution How can I get a resource "Folder" from inside my jar File? . Both of these methods work together to product the result. I have tested this and it works.
public class Main {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
final String pathPartOne = "test/com";
final String pathPartTwo = "/MyResources";
String pathName = "C:\\Users\\Jonathan\\Desktop\\test.jar";
JarTest test = new JarTest();
final File jarFile = new File(pathName);
if(jarFile.isFile()) { // Run with JAR file
final JarFile jar = new JarFile(jarFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
final String name = entries.nextElement().getName();
if (name.startsWith(pathPartOne+pathPartTwo + "/")) { //filter according to the path
if(name.contains("."))//has extension
{
String relavtivePath = name.substring(pathPartOne.length()+1);
String fileName = name.substring(name.lastIndexOf('/')+1);
System.out.println(relavtivePath);
System.out.println(fileName);
test.copyFile(relavtivePath, "C:\\Users\\Jonathan\\Desktop\\" + fileName);
}
}
}
jar.close();
}
}
}
Hope that helps.
The problem here is different File Systems. C:/Users/Cam/Desktop/mtd.jar is a File in the WindowsFileSystem. Since it is a file, and not a directory, you cannot access a subdirectory inside the file; C:/Users/Cam/Desktop/mtd.jar/resources is only a valid Path if mtd.jar is actually a directory instead of a file.
In order to access something on a different file system, you must use the path from the root of that file system. For example, if you have a file in D:\dir1\dir2\file, you cannot reach it using a path that begins with C:\ (symbolic links not withstanding); you must use a path that starts at the root of that file system D:\.
A jar file is just a file. It can be located anywhere within a file system, and can be moved, copied or deleted like any regular file. However, it contains within itself its own file system. There is no windows path that can be used to reference any file inside the jar's file system, just like no path starting at C:\ can reference any file within the D:\ file system.
In order to access the contents of a jar, you must open the jar as a ZipFileSystem.
// Autoclose the file system at end of try { ... } block.
try(FileSystem zip_fs = FileSystems.newFileSystem(pathToZipFile, null)) {
}
Once you have zip_fs, you can use zip_fs.getPath("/path/in/zip"); to get a Path to a file within it. This Path object will actually be a ZipFileSystemProvider path object, not a WindowsFileSystemProvider path object, but otherwise it is a Path object that can be opened, read from, etc., at least until the ZipFileSystem is closed. The biggest differences are that path.getFileSystem() will return the ZipFileSystem, and that resolve() and relativize() cannot use path objects where getFileSystem() returns different file systems.
When your project ran from Eclipse, all the resources were in the WindowsFileSystem, so walking the file system tree and copying the resources was straight forward. When your project ran from a jar, the resources were not in the default file system.
Here is a Java class that will copy resources to an installation directory. It will work in Eclipse (with all the resources as individual files), as well as when the application is packaged into a jar.
public class Installer extends SimpleFileVisitor<Path> {
public static void installResources(Path dst, Class<?> cls, String root) throws URISyntaxException, IOException {
URL location = cls.getProtectionDomain().getCodeSource().getLocation();
if (location.getProtocol().equals("file")) {
Path path = Paths.get(location.toURI());
if (location.getPath().endsWith(".jar")) {
try (FileSystem fs = FileSystems.newFileSystem(path, null)) {
installResources(dst, fs.getPath("/" + root));
}
} else {
installResources(dst, path.resolve(root));
}
} else {
throw new IllegalArgumentException("Not supported: " + location);
}
}
private static void installResources(Path dst, Path src) throws IOException {
Files.walkFileTree(src, new Installer(dst, src));
}
private final Path target, source;
private Installer(Path dst, Path src) {
target = dst;
source = src;
}
private Path resolve(Path path) {
return target.resolve(source.relativize(path).toString());
}
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path dst = resolve(dir);
Files.createDirectories(dst);
return super.preVisitDirectory(dir, attrs);
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Path dst = resolve(file);
Files.copy(Files.newInputStream(file), dst, StandardCopyOption.REPLACE_EXISTING);
return super.visitFile(file, attrs);
}
}
Called as:
Path dst = Paths.get("C:\\Program Files\\mtd");
Installer.installResources(dst, Game.class, "resources");
I FINALLY FOUND THE ANSWER
I don't want to type out a big, long explanation but for anyone looking for the solution, here it is
`
//on startup
installDir("");
for (int i = 0; i < toInstall.size(); i++) {
File f = toInstall.get(i);
String deepPath = f.getPath().replace(f.getPath().substring(0, f.getPath().lastIndexOf("resources") + "resources".length() + 1), "");
System.out.println(deepPath);
System.out.println("INSTALLING: " + deepPath);
installDir(deepPath);
System.out.println("INDEX: " + i);
}
public void installDir(String path) {
System.out.println(path);
final URL url = getClass().getResource("/resources/" + path);
if (url != null) {
try {
final File apps = new File(url.toURI());
for (File app : apps.listFiles()) {
System.out.println(app);
System.out.println("copying..." + app.getPath() + " to " + pfFolder.getPath());
String deepPath = app.getPath().replace(app.getPath().substring(0, app.getPath().lastIndexOf("resources") + "resources".length() + 1), "");
System.out.println(deepPath);
try {
File f = new File(resources.getPath() + "/" + deepPath);
if (getExtention(app) != null) {
FileOutputStream resourceOS = new FileOutputStream(f);
byte[] byteArray = new byte[1024];
int i;
InputStream classIS = getClass().getClassLoader().getResourceAsStream("resources/" + deepPath);
//While the input stream has bytes
while ((i = classIS.read(byteArray)) > 0)
{
//Write the bytes to the output stream
resourceOS.write(byteArray, 0, i);
}
//Close streams to prevent errors
classIS.close();
resourceOS.close();
} else {
System.out.println("new dir: " + f.getPath() + " (" + toInstall.size() + ")");
f.mkdir();
toInstall.add(f);
System.out.println(toInstall.size());
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (URISyntaxException ex) {
// never happens
}
}
}`
Related
I am working on an application that allows users to upload files. I want to keep the uploaded files organized into pre-created folders named for the group that the user belongs to. I can't seem to find a way to make the path editable so that I can pass the group's name into the method as a parameter and have the file stored in that directory.
Here's my latest attempt that results in a "Failed to store file file] with root cause" exception.
#Service
public class StorageServiceImpl implements StorageService {
#Value("${upload.path}")
private Path path;
public void uploadFile(MultipartFile file,String contentName, String groupName){
//make so that files are stored in path/groupname
this.path = Paths.get(this.path.toString() +"/"+groupName +"/");
String filename = contentName+"-"+StringUtils.cleanPath(file.getOriginalFilename());
System.out.println("\n\n\n\n\n"+filename + "\n\n\n");
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file");
}
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"Cannot store file with relative path outside current directory "
+ filename);
}
try (InputStream inputStream = file.getInputStream()) {
System.out.println("\n\n\n\n\n"+this.path.resolve(filename) + "\n\n\n");
Files.copy(inputStream, this.path.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
}
}catch (IOException e) {
String msg = String.format("Failed to store file %s", file.getName());
throw new StorageException(msg, e);
}
}
}
Note: If the directory of the groupName is created before this method runs (as I intend to have it created when the group is created) then the method attempts to store the file in another directory of the same name inside that directory such as:
backend/src/main/webapp/WEB-INF/TestGroup/TestGroup/test.jpg
See how TestGroup shows up twice
Instead of doing this, here you are trying to inject value to a varible of type Path.
#Value("${upload.path}")
private Path path;
how about reading the value to a String variable and storing it as a File and then using it to upload like below,
#Value("${upload.path}")
private String path;
And then using it
public void uploadFile( MultipartFile file, String contentName, String groupName )
{
String filename = contentName + "-" + StringUtils.cleanPath( file.getOriginalFilename() );
// use a File object here
File uploadFilePath = Paths.get( new File( path ).getPath() + "/" + groupName + "/" + filename ).toFile();
try
{
try( InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream( uploadFilePath ) )
{
// Leverage the support of Spring's FileCopyUtils Here
FileCopyUtils.copy( in, out );
}
catch( IOException ex )
{
throw new RuntimeException( ex );
}
}
catch( IOException e )
{
String msg = String.format( "Failed to store file %s", file.getName() );
throw new StorageException( msg, e );
}
}
I got it working by using the absolutePath method from the private Path path to generate the string.
public void uploadFile(MultipartFile file,String contentName, String groupName){
//make so that files are stored in path/groupname
String filename = contentName+"-"+StringUtils.cleanPath(file.getOriginalFilename());
File uploadFilePath = Paths.get(this.path.toAbsolutePath() + "/" + groupName+"/" + filename).toFile();
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file");
}
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"Cannot store file with relative path outside current directory "
+ filename);
}
try (InputStream inputStream = file.getInputStream();OutputStream out = new FileOutputStream(uploadFilePath);) {
FileCopyUtils.copy(inputStream,out);
}
}catch (IOException e) {
String msg = String.format("Failed to store file %s", file.getName());
throw new StorageException(msg, e);
}
}
My file structure:
This is how it looks using netbeans project:
-src
-images
-*.jpg
-stock
-*.java
-images (exact copy of -images)
and here is my jar
-jar
-images
-*.jpg
-stock
-*.java
-images (folder is created but files don't get copied)
My files imagesCopy is the one that I create and ImagesOrg is the one inside .jar / src
File imagesCopy = new File("images");
File imagesOrg = new File(URLDecoder.decode(getClass().getResource("/images").getPath()));
if (!imagesCopy.exists()) {
imagesCopy.mkdir();
for(final File child : imagesOrg.listFiles()) {
try{
Files.copy(child.toPath(), Paths.get(imagesCopy.getAbsolutePath()+"/"+child.getName()), REPLACE_EXISTING);
}catch(Exception e){
System.out.println(e);
}
}
}
The problem definitely lies with:
File imagesOrg = new File(URLDecoder.decode(getClass().getResource("/images").getPath()));
When compiling it it gives me, which is the proper directory
D:\Code\build\classes\images
which is the right directory, but when using this program from jar file I get:
D:\Code\dist\file:\D:\Code\dist\egz.jar!\images
and I assume that it should just be:
D:\Code\dist\egz.jar!\images
without that first part
Probably the simplest way to do it is like this:
public static void main(String[] args) throws URISyntaxException, IOException {
File imagesCopy = new File("C:\\Users\\<YOURNAMEHERE>\\images");
URI uri = ImageCopy.class.getResource("/images").toURI();
if (!uri.toString().startsWith("file:")) {
Map<String, String> env = new HashMap<>();
env.put("create", "true");
FileSystems.newFileSystem(uri, env);
}
Path imagesOrg = Paths.get(uri);
System.out.println(imagesOrg);
if (!imagesCopy.exists()) {
imagesCopy.mkdir();
try(DirectoryStream<Path> paths = Files.newDirectoryStream(imagesOrg)) {
for (final Path child : paths) {
System.out.println(child);
try {
String targetPath = imagesCopy.getAbsolutePath() + File.separator + child.getFileName().toString();
System.out.println(targetPath);
Files.copy(child, Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
It's not super-pretty, but it works. Might need to fiddle with the code if you have nested directories.
Note that you must create the FileSystem before accessing it (as per the Oracle Docs). I don't know why this is required, but there we go.
I've tested this and it will copy files from inside your JAR to wherever you would like.
Here is a simple code to do it. You can adapt as you need.
package br.com.jjcampos.main;
//imports here
public class CopyImage {
private static ClassLoader loader = CopyImage.class.getClassLoader();
public static void main(String[] args) throws IOException {
InputStream stream = loader.getResourceAsStream("br/com/jjcampos/images/test.jpg");
OutputStream outputStream =
new FileOutputStream(new File("c:/temp/newImage.jpg"));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.close();
}
}
Understand that you can't copy a source from a stream (your jar) as a list of files. Unless you want to unpack it first. My suggestion is you to add a txt file with the list of your images then you read this file and use suggested code to copy each one.
Something like this:
public class CopyImage {
private static ClassLoader loader = CopyImage.class.getClassLoader();
public static void main(String[] args) throws IOException {
copyImages("c:/temp/");
}
public static void copyImages(String pathDestiny) throws IOException{
InputStream listOfFiles = loader
.getResourceAsStream("br/com/jjcampos/images/listImages.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(listOfFiles));
String line;
while ( (line = reader.readLine())!=null ){
InputStream stream = loader.getResourceAsStream("br/com/jjcampos/images/"
+ line);
OutputStream outputStream =
new FileOutputStream(new File(pathDestiny + line));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.close();
}
}
}
And your listImages.txt with
test.jpg
And you should decide if you put the full path on the text file or not to use in your code.
I am running Mac OSX Mavericks. Right now I am creating a JAR file from a folder (org, the package). When I use this code from here:
public void run() throws IOException
{
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream("/Users/username/Library/Application Support/VSE/temp/output.jar"), manifest);
add(new File("/Users/username/Library/Application Support/VSE/temp/org"), target);
target.close();
}
private void add(File source, JarOutputStream target) throws IOException
{
BufferedInputStream in = null;
try
{
if (source.isDirectory())
{
String name = source.getPath().replace("\\", "/");
if (!name.isEmpty())
{
if (!name.endsWith("/"))
name += "/";
JarEntry entry = new JarEntry(name);
entry.setTime(source.lastModified());
target.putNextEntry(entry);
target.closeEntry();
}
for (File nestedFile: source.listFiles())
add(nestedFile, target);
return;
}
JarEntry entry = new JarEntry(source.getPath().replace("\\", "/"));
entry.setTime(source.lastModified());
target.putNextEntry(entry);
in = new BufferedInputStream(new FileInputStream(source));
byte[] buffer = new byte[1024];
while (true)
{
int count = in.read(buffer);
if (count == -1)
break;
target.write(buffer, 0, count);
}
target.closeEntry();
}
finally
{
if (in != null)
in.close();
}
}
When I extract the JAR file, There is a META-INF folder, but instead of having the org folder in the extracted jar, I have my Users folder copied into it (except because of it's size, its wasn't filled with all my stuff and my application crashed). I'm expecting this is because the code was written for a Windows system, and the differences with the filesystem (such as \ or /). How would I make the code include only the "org" directory, and not everything leading up to it?
Provided you use Java 7+ you may easily do this by using one of my packages in combination with the zip filesystem provider of the JDK to create it:
private static final Map<String, ?> ENV = Collections.singletonMap("create", "true");
public void run()
throws IOException
{
final Path zipPath = Paths.get("/Users/username/Library/Application Support/VSE/temp/output.jar");
final Path srcdir = Paths.get("/Users/username/Library/Application Support/VSE/temp/org");
final URI uri = URI.create("jar:" + zipPath.toUri());
Files.deleteIfExists(zipPath);
try (
final FileSystem zipfs = FileSystems.newFileSystem(uri, ENV);
) {
copyManifest(zipfs);
copyDirectory(srcdir, zipfs);
}
}
private void copyManifest(final FileSystem zipfs)
throws IOException
{
final Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
Files.createDirectory(zipfs.getPath("META-INF/");
try (
final OutputStream out = Files.newOutputStream(zipfs.getPath("META-INF/MANIFEST.MF"));
) {
manifest.write(out);
}
}
private void copyDirectory(final Path srcdir, final FileSystem zipfs)
{
final String lastName = srcdir.getFileName().toString();
final Path dstDir = zipfs.getPath(lastName);
Files.createDirectory(dstDir);
MoreFiles.copyRecursive(srcDir, dstDir, RecursionMode.FAIL_FAST);
}
I am new to Java, I tried to practice some example but I am facing issue in the file handling topic.
Following is the example that what I am trying.
T1--> T2--> T3--> T4--> Gan--> q.txt
|
--> Lin-->Img-->s.png
|
--> p.txt
This is the folder structure.
And I want output in the following format.
p.txt
Lin/Img/s.png
Gen/q.txt
That means when the first file is getting in any directory, after that next file will be printed with the path from first file is got.
The above directory structure is not fixed. It may change.
Now following are code that I have did but I am not getting proper output:
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
class FileProgram {
private ArrayList<File> listOfDirectories = new ArrayList<File>();
private ArrayList<File> rawFiles = new ArrayList<File>();
private ArrayList<String> parentDir = new ArrayList<String>();
private ArrayList<String> filesToDisplay = new ArrayList<String>();
private Iterator i, listDir;
private boolean firstFile = false;
private String parents = "";
public void getDetails(File file) {
try {
if (file.exists()) {
File directoies[] = file.listFiles();
if (!rawFiles.isEmpty()) {
rawFiles.clear();
}
for (File f : directoies) {
rawFiles.add(f);
}
i = rawFiles.iterator();
while (i.hasNext()) {
File isFile = (File) i.next();
if (isFile.isFile()) {
displayFiles(isFile);
}
if (isFile.isDirectory()) {
listOfDirectories.add(isFile);
}
}
iterateInnerDirectories();
} else {
System.out.println("Invalid File Path");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
FileProgram ray = new FileProgram();
ray.getDetails(new File("D:\\Temp"));
}
private void iterateInnerDirectories() {
listDir = listOfDirectories.iterator();
while (listDir.hasNext()) {
File isFile = (File) listDir.next();
File f = isFile;
listOfDirectories.remove(isFile);
getDetails(isFile);
}
}
private void displayFiles(File file) {
if (firstFile == false) {
firstFile = true;
String rootPath = file.getParent();
rootPath = rootPath.replace(file.getName(), "");
parentDir.add(rootPath);
parents = file.getParentFile().getName();
System.out.println(file.getName());
filesToDisplay.add(file.getName());
} else {
String rootPath = file.getParent();
rootPath = rootPath.replace(file.getName(), "");
if (parentDir.contains(rootPath)) {
parents = file.getParentFile().getName();
System.out.println(file.getName());
filesToDisplay.add(file.getName());
} else {
System.out.println(file);
}
}
}
}
Please anybody can help me to get proper output that I have mentioned above.
Thanks in advance.
Unless you're using a Java prior to Java 7 I would strongly suggest to use Path.
You can walk a directory recursively using Files.walkFileTree().
Once you encounter a file (!Files.isDirectory()), you can get its parent with Path.getParent(). And you can print the relative path to this parent of all further file using Path.relativize().
Short, Simple Implementation
In this implementation I don't even use Files.isDirectory() because visitFile() is only called for files:
public static void printFiles(Path start) {
try {
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
Path parent;
#Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
if (parent == null)
parent = file.getParent();
System.out.println(parent.relativize(file));
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
And this is how you call it:
printFiles(Paths.get("/path/to/T1"));
I am new to the nio class, and am having trouble moving a directory of files to a newly created directory.
I first create 2 directories with:
File sourceDir = new File(sourceDirStr); //this directory already exists
File destDir = new File(destDirectoryStr); //this is a new directory
I then try to copy the existing files into the new directory, using:
Path destPath = destDir.toPath();
for (int i = 0; i < sourceSize; i++) {
Path sourcePath = sourceDir.listFiles()[i].toPath();
Files.copy(sourcePath, destPath.resolve(sourcePath.getFileName()));
}
This throws the following error:
Exception in thread "main" java.nio.file.FileSystemException: destDir/Experiment.log: Not a directory
I know that destDir/Experiment.log is not an existing directory; it should be a new file as a result of the Files.copy operation. Could someone point out where my operation is going wrong? Thanks!
You need to use walkFileTree to copy directories. If you use Files.copy on a directory only an empty directory will be created.
Following code taken/adapted from http://codingjunkie.net/java-7-copy-move/
File src = new File("c:\\temp\\srctest");
File dest = new File("c:\\temp\\desttest");
Path srcPath = src.toPath();
Path destPath = dest.toPath();
Files.walkFileTree(srcPath, new CopyDirVisitor(srcPath, destPath, StandardCopyOption.REPLACE_EXISTING));
public static class CopyDirVisitor extends SimpleFileVisitor<Path>
{
private final Path fromPath;
private final Path toPath;
private final CopyOption copyOption;
public CopyDirVisitor(Path fromPath, Path toPath, CopyOption copyOption)
{
this.fromPath = fromPath;
this.toPath = toPath;
this.copyOption = copyOption;
}
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
Path targetPath = toPath.resolve(fromPath.relativize(dir));
if( !Files.exists(targetPath) )
{
Files.createDirectory(targetPath);
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
Files.copy(file, toPath.resolve(fromPath.relativize(file)), copyOption);
return FileVisitResult.CONTINUE;
}
}
Simply make the destination directory if it doesn't exist.
File sourceDir = new File(source); //this directory already exists
File destDir = new File(dest); //this is a new directory
destDir.mkdirs(); // make sure that the dest directory exists
Path destPath = destDir.toPath();
for (File sourceFile : sourceDir.listFiles()) {
Path sourcePath = sourceFile.toPath();
Files.copy(sourcePath, destPath.resolve(sourcePath.getFileName()));
}
Note that sourceDir.listFiles() will also return directories, which you will either want t recurse into, or ignore...
This is my solution for recursively moving a directory from source to target. It works like a charm.
public static void move(Path source, Path target) throws IOException {
class FileMover extends SimpleFileVisitor<Path> {
private Path source;
private Path target;
private FileMover(Path source, Path target) {
this.source = source;
this.target = target;
}
#Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
Files.move(file, target.resolve(source.relativize(file)),
StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
Path newDir = target.resolve(source.relativize(dir));
try {
Files.copy(dir, newDir,
StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING);
} catch (DirectoryNotEmptyException e) {
// ignore and skip
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
Path newDir = target.resolve(source.relativize(dir));
FileTime time = Files.getLastModifiedTime(dir);
Files.setLastModifiedTime(newDir, time);
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
}
FileMover fm = new FileMover(source, target);
EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
Files.walkFileTree(source, opts, Integer.MAX_VALUE, fm);
}
for (int i = 0; i < sourceSize; i++) {
Path sourcePath = sourceDir.listFiles()[i].toPath();
Files.copy(sourcePath, destPath.resolve(sourcePath.getFileName()));
}
This is very strange code. You have already got a file count from somewhere, in sourceSize, yet you are calling listFiles() for every iteration. I would have expected something more like this:
for (File file : sourceDir.listFiles()) {
Path sourcePath = file.toPath();
Files.copy(sourcePath, destPath.resolve(sourcePath.getFileName()));
}