glf4k
glf4k

Reputation: 449

File written to ZIP file available only during runtime [Java]

I am having issue with writting a binary file to simple ZIP archive using Java 7's FileSystem and Files API.

The problem occur while performing write operation, which throws no exception at all, the file is not written to the ZIP archive, but is available in runtime (Files.exists(backup) returns true and it's possible to read the file using Files.readAllBytes(backup) ).

When program is closed and relaunched, the file is not available anymore.

Snippet

This method should create backup of any path, no matter who is the FileSystem provider, 'fails' just on paths inside ZIP archives.

/**
 * Creates backup of path provided by 'file' parameter.
 *
 * @param file input file requiring backup creation
 * @return backup file
 * @throws java.io.IOException thrown in case of unsuccessful backup attempt
 */
public static Path createBackup(Path file) throws IOException {

    FileSystem fileSystem = file.getFileSystem();
    Path backup = fileSystem.getPath(file.toString() + ".BAK");

    return Files.write(backup, Files.readAllBytes(file));

}

public static void main(String... args) {
   try {

      Path f = FileSystems.newFileSystem(Paths.get("a.zip"), null).getPath("file.bin");

      Path backup = createBackup(f);

      System.out.println(Files.exists(backup)); // prints "true"
      System.out.println(new String(Files.readAllBytes(backup))); // prints its bytes
      System.out.println(backup.toString()); // prints "file.bin.BAK"
   } catch (IOException ex) {
      System.err.println(ex);
   }

}

But the file does not physically exists in the ZIP.

EDIT: I've managed to make it work, but there is a problem. Code below closes the file system, but writes properly. There is need to "refresh"/"reopen" filesystem somehow.

public static Path createBackup(Path file) throws IOException {

   try(FileSystem fileSystem = file.getFileSystem()) {

      Path backup = fileSystem.getPath(file.toString() + ".BAK");
      return Files.write(backup, Files.readAllBytes(file));

   }
}

When original method is keept and file system is closed manually after everything is done, it removes the zip file and keeps something like zipfstmp***.tmpand throws:

java.nio.file.FileAlreadyExistsException: zipfstmp2666831581340533856.tmp -> a.zip

When the tmp file is renamed to "a.zip", its a valid modified archive.

Upvotes: 2

Views: 1139

Answers (2)

Holger
Holger

Reputation: 298459

You should close using the try-with-resource statement within the caller, which created the file system. There is no need to deal with file systems in the createBackup method at all.

public static Path createBackup(Path file) throws IOException {
    Path backup = file.resolveSibling(file.getFileName().toString()+".BAK");
    return Files.copy(file, backup, StandardCopyOption.REPLACE_EXISTING);
}
public static void main(String... args) {
    try(FileSystem fs = FileSystems.newFileSystem(Paths.get("a.zip"), null)) {
       Path f = fs.getPath("file.bin");
       Path backup = createBackup(f);

       System.out.println(Files.exists(backup)); // prints "true"
       System.out.println(new String(Files.readAllBytes(backup))); // prints its bytes
       System.out.println(backup.toString()); // prints "file.bin.BAK"
    } catch (IOException ex) {
       System.err.println(ex);
    }
}

Upvotes: 3

Andrew S
Andrew S

Reputation: 2987

Below is a simple example where all the logic is in the main method.

The key to solving this is in using a URI to state explicitly what kind of File System provider to use. You may wonder why jar and not zip. In Java a jar files are in fact zip files but with a different file extension. So java can use the same mechanisms to access zip files.

I tried this with binary files and text files and it seems to work on my Win 10 machine. Just change the source directory of the zip file (///e:/a.zip) to point to the location of your zip file. For me the file.bin.BAK was written to the root of eclipse project directory (you might want to change that also for deployment).

import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String... args) {

            //Create URI to define the source as a Zip file.
            //I've used jar because this is essentially a zip file.
            Map<String, String> env = new HashMap<>(); 
            env.put("create", "true"); 
            URI uri = URI.create("jar:file:///e:/a.zip");

            try{

                //Build paths to source file in zip  and externally.
                Path pathInZipfile = FileSystems.newFileSystem(uri, env).getPath("/file.bin");  
                Path extPath  = Paths.get("file.bin.BAK");


                //If file does not exist create it.
                if (! Files.exists(extPath))
                    Files.createFile(extPath);


                //Copy the files over.
                 Files.copy( pathInZipfile, extPath,
                            StandardCopyOption.REPLACE_EXISTING ); 

            }//end try
            catch (IOException ex){
                ex.printStackTrace();
            }//enc catch

    }//end main
}//end class

Upvotes: 0

Related Questions