7

I need to copy a folder, packed in a Jar on runtime. I want to do it by calling a function in a class which is also contained in the same folder.

I've tried using getSystemResource:

URL sourceDirUrl = ClassLoader.getSystemResource("sourceDirName"); 
File sourceDir = new File(sourceDirUrl.toURI());

but it doesn't work. I probably have to use getResourceAsStream function recursively. Is there a more elegant/strait-forward way to do this?

In case I have to do it recursively: 1. I don't want to specify the files hard-coded, I want to do it dynamically 2. I don't want to create a separate archive. I want this resource to be in the same Jar as the classes dealing with it

Thanks

I ended up doing what Koziołek suggested below. Although I was hoping for a more elegant solution, but it looks like this is as good as it gets.

Leonid B
  • 325
  • 1
  • 4
  • 14
  • are you giving `sourceDirName` as complete path? – Nishant Mar 21 '11 at 11:44
  • yeah, please give an example for the `sourceDirName` you used. – asgs Mar 21 '11 at 11:46
  • @Nishant - no, a relative path, the first line works, the URL is correct, it's impossible however to create a File object based on this URL, since the folder is inside the archive – Leonid B Mar 21 '11 at 11:50

3 Answers3

5

Using the classloader you cannot retrieve a folder as it can not be a resource of your classpath.

Several solutions are possible:

  • Using the classloader getResource method, retrieve all resources of your folder one by one if you know in advance the file names you are looking for.
  • Pack your complete folder into an archive that you can retrieve from the classloader using the previous method.
  • Unzip your jar directly to retrieve the contained folder. It requires to know the precise location of the jar from the filesystem. This is not always possible depending on the application and is not portable.

I would preferably going for the second solution that is more portable and flexible but requires to repack the archive for all modifications of the folder content.

greydet
  • 5,509
  • 3
  • 31
  • 51
  • 1. I don't want to specify the files hard-coded, I want to do it dynamically 2. I don't want to create a separate archive. I want this resource to be in the same Jar as the classes dealing with it – Leonid B Mar 21 '11 at 11:59
  • That's why I would prefer the second proposed solution ;) – greydet Mar 21 '11 at 12:01
2

Jar is simple ZIP file. You can use java.util.zip.* package to decompress files.

Koziołek
  • 2,791
  • 1
  • 28
  • 48
  • This way the location of the jar needs to be hardcoded in the application. – greydet Mar 21 '11 at 12:03
  • 1
    nope... You can use getResource like method to get jar location. – Koziołek Mar 21 '11 at 12:09
  • I would be interested to know how to do that by a method as simple as the getResource method from the classloader and considering all possible cases of jar packaging. – greydet Mar 21 '11 at 12:21
  • Do you really think that the proposed code to retrieve the location is simpler than a getResource? Moreover this method supposes that the jar is accessible directly from the filesystem. – greydet Mar 21 '11 at 12:41
  • this method supposes that the jar is accessible directly from the filesystem - but need we more? if jar is only on memory then method is not good enoght. But in this case it is all we need. – Koziołek Mar 21 '11 at 13:00
0

I had the same problem, there are various solutions proposed if you browse SO, usually not so simple to implement. I tried several of them and finally the best for me was the simplest:

  • pack the folder content in a .zip file
  • put the.zip file as a resource file in the .jar
  • access the .zip file as a resource file and extract it using the ZipInputStream API.

Here a generic method to do this:

   /**
    * Extract the contents of a .zip resource file to a destination directory.
    * <p>
    * Overwrite existing files.
    *
    * @param myClass     The class used to find the zipResource.
    * @param zipResource Must end with ".zip".
    * @param destDir     The path of the destination directory, which must exist.
    * @return The list of created files in the destination directory.
    */
   public static List<File> extractZipResource(Class myClass, String zipResource, Path destDir)
   {
      if (myClass == null || zipResource == null || !zipResource.toLowerCase().endsWith(".zip") || !Files.isDirectory(destDir))
      {
         throw new IllegalArgumentException("myClass=" + myClass + " zipResource=" + zipResource + " destDir=" + destDir);
      }

      ArrayList<File> res = new ArrayList<>();

      try (InputStream is = myClass.getResourceAsStream(zipResource);
              BufferedInputStream bis = new BufferedInputStream(is);
              ZipInputStream zis = new ZipInputStream(bis))
      {
         ZipEntry entry;
         byte[] buffer = new byte[2048];
         while ((entry = zis.getNextEntry()) != null)
         {
            // Build destination file
            File destFile = destDir.resolve(entry.getName()).toFile();

            if (entry.isDirectory())
            {
               // Directory, recreate if not present
               if (!destFile.exists() && !destFile.mkdirs())
               {
                  LOGGER.warning("extractZipResource() can't create destination folder : " + destFile.getAbsolutePath());
               }
               continue;
            }
            // Plain file, copy it
            try (FileOutputStream fos = new FileOutputStream(destFile);
                    BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length))
            {
               int len;
               while ((len = zis.read(buffer)) > 0)
               {
                  bos.write(buffer, 0, len);
               }
            }
            res.add(destFile);
         }
      } catch (IOException ex)
      {
         LOGGER.log(Level.SEVERE, "extractZipResource() problem extracting resource for myClass=" + myClass + " zipResource=" + zipResource, ex);
      }
      return res;
   }
jjazzboss
  • 1,261
  • 8
  • 14