Search code examples
javafile-copying

Java copy files into zip with Files


How do I copy a file from D:\test\ folder to D:\test.zip using java.nio.files.Files? I got NoSuchFileException: D:\Ausbildungsnachweise\Ausbildungsnachweis_Technisch_Form.doc -> D:\test.zip\Ausbildungsnachweis_Technisch_Form.doc as exception. I think i am using the right pathes, but no idea why this error occurs, since the paths do exist.

My whole method:

Map<String, String> env = Collections.singletonMap("create", "true");
Path dir = Paths.get(destinationFolder.getAbsolutePath());
Path destination = dir.resolve(zipNameGenerator.getName() + fileEnding);
URI uri = URI.create("jar:" + destination.toUri());

try {
    FileSystem fs;
    if (overwriteZipWithSameName || !Files.exists(destination)) {
        fs = FileSystems.newFileSystem(uri, env);
    } else {
        fs = FileSystems.getFileSystem(uri);
    }
    for (String file : sourceFolder.list(filenameFilter)) {
        Files.copy(sourceFolder.toPath().resolve(file), fs.getPath(file));
    }
} catch (IOException e1) {
    e1.printStackTrace();
}

Solution

  • URI uri = new URI("jar:file:/D:/test.zip");
    

    No, it should be jar:file:///D:/test.zip. But the most portable way of doing it is:

    Path dir = Paths.get("D:\\");
    Path file = dir.resolve("test.zip");
    URI uri = URI.create("jar:" + file.toUri());
    

    Similarly, where you do:

    Paths.get(sourceFolder + "\\" + file)
    

    it's more portable to do:

    sourceFolder.resolve(file)
    

    (for example, if you run the application on Linux in the future, the "\" won't work.)

    Also, you could make your map more succinctly like this:

    Map<String, String> env = Collections.singletonMap("create", "true");
    

    UPDATE:

    Following your updated code and your comment below, I've created a working main class, shown below, reusing as much of your existing code as possible while correcting where necessary. Note that you need to use newFileSystem() even for existing zip files. The getFileSystem() call assumes you've already created a FileSystem object somewhere else in your code, which is cached by the file system provider, and FileSystems.getFileSystem() returns a reference to the already existing object. Also, FileSystem is Closeable, so use it in a try-with-resources statement.

    Complete class, fully working:

    import java.io.File;
    import java.io.FilenameFilter;
    import java.io.IOException;
    import java.net.URI;
    import java.nio.file.*;
    import java.util.Collections;
    import java.util.Map;
    
    public class SOQn47577298 {
        private final static Map<String, String> CREATE_TRUE =
                Collections.singletonMap("create", "true");
    
        interface ZipNameGenerator {
            String getName();
        }
    
        static void copyFileToZip(
                File sourceFolder,
                FilenameFilter filenameFilter,
                File destinationFolder,
                ZipNameGenerator zipNameGenerator,
                String fileEnding,
                boolean overwriteZipWithSameName) throws IOException {
    
            Path dir = Paths.get(destinationFolder.getAbsolutePath());
            Path destination = dir.resolve(zipNameGenerator.getName() + fileEnding);
            URI uri = URI.create("jar:" + destination.toUri());
    
            final Map<String, String> env;
            if (overwriteZipWithSameName || !Files.exists(destination)) {
                env = CREATE_TRUE;
            } else {
                env = Collections.emptyMap();
            }
    
            try (FileSystem fs = FileSystems.newFileSystem(uri, env)){
                for (String file : sourceFolder.list(filenameFilter)) {
                    Path source = sourceFolder.toPath().resolve(file);
                    Path dest = fs.getPath(file);
                    Files.copy(source, dest);
                }
            }
        }
    
        public static void main(String[] args) throws IOException {
            final File source = new File("D:\\Ausbildungsnachweise");
            final FilenameFilter nameFilter = (dir, name) -> name.endsWith(".doc");
            final File dest = new File("D:\\");
            final ZipNameGenerator zipNameGnr = () -> "test";
            final String fileEnding = ".zip";
            final boolean overwrite = false;
    
            copyFileToZip(source, nameFilter, dest, zipNameGnr, fileEnding, overwrite);
        }
    }