ZipXap Single-File Virtual File System

ZX-VFS is an free and open source (FOSS) Java API that implements a high performance single-file virtual file system licensed under the LGPL license.  It uses its own proprietary file format to support fast random file access.  To reduce archive size, it can utilize GZip compression.  It supports password based encryption (PBE with MD5 and DES).  It is extremely easy to use, and an ideally suited storage solution for desktop-based applications (such as ZxApp IT).



Download Javadoc


Latest Release: version 1.0.0, 5/9/2010 - See Whats new in version 1.0.0.

 

Getting Started

To use, simply instantiate a VirtualFileSystem using the following constructor:


            public VirtualFileSystem(java.io.File archiveFile, boolean createIfNecessary,

                               boolean synchronousMode, java.lang.String userId)


For example:


      File myArchiveFile=new File("c:\\myarchive.arc");

      VirtualFileSystem vfs=new VirtualFileSystem(myArchiveFile, true, true, "user");


New in version 0.99 is that vfs automatically locks the archive file so that no other instances of VirtualFileSystem can open the same archive file.  VFS creates a "lock" file to tell other instances that an archive file is being used.  The lock file will be deleted if when vfs.close() is called, or else when the virtual machine is shut down.  If the virtual machine is terminated abnormally, such as by terminating a process,  then a stale lock file will remain.  This means you should do one of two things.  


(1) The first option is to perform a check to ensure that the file isn't locked before opening it, and if so then offer the user the opportunity to remove the lock:


      if (VirtualFileSystem.isLocked(myArchiveFile))

      {

            if (JOptionPane.showConfirmDialog(getFrame(),

                  "Archive '"+myArchiveFile+"' is already locked by another instance. \n"+

                  "Would you like to claim the lock?", "Claim Lock?", JOptionPane.YES_NO_OPTION)==

                  JOptionPane.NO_OPTION)

            {

                  JOptionPane.showMessageDialog(getFrame(), "Archive '"+myArchiveFile+

                        "' is already opened by another application.",

                        "Cannot Open Archive", JOptionPane.ERROR_MESSAGE);

                  return;

            }

            VirtualFileSystem.removeLock(myArchiveFile);

      }


(2) The second option would be to instantiate the VirtualFileSystem without creating a lock.  This can be done safely if you've taken other steps prevent multiple instances of your application from running, or to ensure that only one of the instances will be performing updates.  Also keep in mind that if there is a second instance that is performing only reads, it will cease to be reliable as soon as an update is made from the other instance.  Here is the constructor that provides the option of instantiating without a lock:


      public VirtualFileSystem(java.io.File archiveFile,

      boolean createIfNecessary,

      boolean synchronousMode,

      java.lang.String userId,

      boolean lockArchiveFile)




If the archive does not exist, it will be created.  Of course you can check to see if a file is a valid archive by calling;


      vfs.isVirtualFileSystem()


There is a third constructor that needs to be used if the file is encrypted;


            public VirtualFileSystem(

                  java.io.File archiveFile,

                  boolean createIfNecessary,

                  boolean synchronousMode,

                  java.lang.String userId,

                  boolean complainIfNotAVirtualFileSystemFile,

                  boolean useCompression,

                  CipherSettings cipherSettings,

                  boolean lockArchiveFile)


When opening a password protected archive, call vfs.isValidated().  If this method returns true then you know the correct password was supplied.  if the method returns false, then you know to inform the user and prompt them for a correct password.


The Java IO API uses the java.io.File object to represent both files and directories.  ZX-VFS uses two distinct objects, VFSFile and VFSRepository, to represent files and directories respectively. To simplify life, the ZX-VFS directory separator character is ALWAYS '/', and is not platform dependent.


You can get the root repository by calling the vfs.getRootRepository() method.

     

      VFSRepository rep=vfs.getRootRepository()


Or you can get a repository by name with the vfs.getRepository() method.


      VFSRepository rep=vfs.getRepository("my_repository")


You could, of course, just go directly for a file by calling the vfs.getFile() method.


      VFSFile file=vfs.getFile("my_repository/my_file.jpg")


Or you can get a file within a repository by calling the rep.getFile() method.


      VFSFile file=rep.getFile("myfile.jpg");


Once you have a file, you can get the contents of it using one of two methods:


      public java.io.InputStream getInputStream()

      public byte[] getContent()


If memory consumption is a concern then getInputStream() is the recommended method.


You can update the content of a VFSFile by using one of the following methods:


      public java.io.OutputStream getOutputStream()

      public void updateContent(java.util.List beansToSerialize)

      public void updateContent(byte[] content)

      public void updateContent(java.io.File fileSystemContent)

      public void updateContent(java.io.InputStream inputStream)


If memory consumption is a concern then you should avoid using updateContent(byte[] content).

     

Lastly, there are a number of saveFile() methods within a VFSRepository that allow you to create (and update) VFSFile;


      VFSFile saveFile(java.lang.String fileName, byte[] content)

      VFSFile saveFile(java.lang.String fileName, byte[] content, boolean overwriteExisting)

      VFSFile saveFile(java.lang.String fileName, java.io.File fileSystemFile)

      VFSFile saveFile(java.lang.String fileName, java.io.File fileSystemFile, boolean overwriteExisting)

      VFSFile saveFile(java.lang.String fileName, java.io.InputStream inputStream)

      VFSFile saveFile(java.lang.String fileName, java.io.InputStream inputStream, boolean overwriteExisting)

      VFSFile saveFile(java.lang.String filename, java.util.List<java.lang.Object> beansToSerialize)
       VFSFile saveFile(java.lang.String fileName, java.io.Reader reader)



Its all quite easy.  But if you want more sample code, download the source code and have a look at the unit test code.  It should provide reasonable documentation of how to use various features, including encryption.


How did ZX-VFS go from version 0.51 to 0.99?

It didn't.  There was a major bug fix going from 0.51 to 0.52.  Unfortunately, 0.52 was never posted.  The requirements were well defined, and so the API has been nearly feature complete since 0.50.  There were fewer bugs than expected, which is a good problem to have.  In going from version 0.52 to 0.99 there were two enhancements. The first is that memory consumption was reduced, on average, by about 30%.  The other enhancement made is a locking mechanism to protect against mishaps.  A data corruption could occur if two different instances of VFS open the same archive at the same time and perform updates.


What is the difference between ZX-VFS and java.io?

The Java IO API uses java.io.File to represent both files and directories.  ZX-VFS uses two distinct objects, VFSFile and VFSRepository, to represent files and directories respectively. To simplify life, the ZX-VFS directory separator character is ALWAYS '/', and is not platform dependent.

 

Whats the difference between ZX-VFS and TrueZip?

They are similar but different.  TrueZip allows you to use a ZIP or TAR file (and possibly others) as a virtual file system.  There are many advantages to using a standard archive format, and for many applications this is, rightfully, the preferred solution. Unfortunately, as a consequence of the archive format, there are some major drawbacks.  The biggest drawback is that performance degrades linearly with the size of the archive file.  When we experimented wth using TrueZip as the backend for ZxApp IT, we found that a 425 meg file took approximately one minute when performing ANY operation on it.  Opening it, querying it, and closing it all took substantial amounts of time.  ZX-VFS, on the other hand, was designed from the ground up to be a high performance vfs.  A larger archive, such as the 425 meg example, takes only slightly longer to load.  Virtual file read-write operations are unaffected by the overall size of the archive.  


In a ZX-VFS archive, performance is optimizing.  In a Zip archive, space is optimized.  As a consequence, a ZX-VFS archive (with compression enabled) will be about 2% or 3% larger than a comparable Zip file.


Whats the difference between ZX-VFS and Apache Commons VFS?

The Apache Commons VFS provides developers with a way of accessing many types of file systems using a single API.  It is possible that a future project will be to write a provider for the Commons VFS to access ZX-VFS archives.  Unfortunately, the Commons VFS provides read-only access to the contents of Zip, Gzip, Bzip, Jar, and Tar files.  Thus, you cannot add, update, or delete files within the archive.


Are there any other limitations I should be aware of?

The current version of ZX-VFS is not thread safe.  This is likely to be implemented in a later version, but for now this was not a requirement.