Copy Directory from a Jar File

Copy directory from a jar file

I think your approach of using a zip file makes sense. Presumably you'll do a getResourceAsStream to get at the internals of the zip, which will logically look like a directory tree.

A skeleton approach:

InputStream is = getClass().getResourceAsStream("my_embedded_file.zip");
ZipInputStream zis = new ZipInputStream(is);
ZipEntry entry;

while ((entry = zis.getNextEntry()) != null) {
// do something with the entry - for example, extract the data
}

Copy/extract directory from resources inside Jar file

Putting all of your asset files into a zip file, as mentioned in the question to which Reddymails linked, is a pretty good way to do it.

If you want to keep the asset files as individual entries in your .jar, the problem is that you will not have a directory to list:

  • A directory entry in a .jar or zip file is just a name; there is no way to “list” it.
  • The .jar file is not always obtainable, because ProtectionDomain.getCodeSource() is allowed to return null.
  • There are complex ClassLoaders that read from sources other than directories or .jar files.

You can have your build process list the entries in a text file (since you know what they are, after all) before packaging them, and include that text file in your .jar. Then copying them at runtime is as easy as reading from that file:

Path assetDir = /* ... */;

try (BufferedReader listFile = new BufferedReader(
new InputStreamReader(
getClass().getResourceAsStream("assets-list.txt"),
StandardCharsets.UTF_8))) {

String assetResource;
while ((assetResource = listFile.readLine()) != null) {
Path assetFile = assetDir.resolve(assetResource);
Files.createDirectories(assetFile.getParent());
try (InputStream asset = getClass().getResourceAsStream(assetResource)) {
Files.copy(asset, assetFile);
}
}
}

Copy directory from a jar file using only pure java

That's a crazy complicated way to do it. It's also dependent entirely on your app being in a jar, which makes testing, deployments into runtime-modularized loaders, etc - tricky.

There are much easier ways to do this. Specifically, SomeClass.class.getResourceAsStream("/foo/bar") will get you an InputStream for the entry /foo/bar in the same classpath root as where the class file representing SomeClass lives - be it a jar file, a plain old directory, or something more exotic.

That's how you should 'copy' your files over. This trivial code:

String theResource = "com/foo/quillionsapp/toCopy/example.txt";
Path targetPath = ....;

try (var in = YourClass.class.getResourceAsStream("/" + theResource)) {
Path tgt = targetPath.resolve(theResource);
Files.createDirectories(tgt.getParent());
try (var out = Files.newOutputStream(tgt)) {
in.transferTo(out);
}
}

Now all you need is a list of all files to be copied. The classpath abstraction simply does not support listing. So, any attempt to hack that in there is just that: A hack. It'll fail when you e.g. have modularized loaders and the like. You can just do that - you're already writing code that asplodes on you if your code is not in a jar file. It's not hard to write a method that gives you a list of all contents for both 'dir on the file system' based classpaths as well as 'jar file' based ones, but there is a ready alternative: Make a text file with a known name that lists all resources at compile time. You can write it yourself, or you can script it. Can be as trivial as ls src/resources/* > filesToCopy.txt or whatnot. You can also use annotation processors to produce such a file.

Once you know the file exists (it's in the jar same as the files you want to copy), read it with getResourceAsStream just the same way, and now you have a list of resources to write out using the above code. That trick means your code is entirely compatible with the API of ClassLoader: You are just relying on the guaranteed functionality of 'get me an inputstream with the full data of this named resource' and nothing else.

How to copy file inside jar to outside the jar?

First of all I want to say that some answers posted before are entirely correct, but I want to give mine, since sometimes we can't use open source libraries under the GPL, or because we are too lazy to download the jar XD or what ever your reason is here is a standalone solution.

The function below copy the resource beside the Jar file:

  /**
* Export a resource embedded into a Jar file to the local file path.
*
* @param resourceName ie.: "/SmartLibrary.dll"
* @return The path to the exported resource
* @throws Exception
*/
static public String ExportResource(String resourceName) throws Exception {
InputStream stream = null;
OutputStream resStreamOut = null;
String jarFolder;
try {
stream = ExecutingClass.class.getResourceAsStream(resourceName);//note that each / is a directory down in the "jar tree" been the jar the root of the tree
if(stream == null) {
throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
}

int readBytes;
byte[] buffer = new byte[4096];
jarFolder = new File(ExecutingClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getPath().replace('\\', '/');
resStreamOut = new FileOutputStream(jarFolder + resourceName);
while ((readBytes = stream.read(buffer)) > 0) {
resStreamOut.write(buffer, 0, readBytes);
}
} catch (Exception ex) {
throw ex;
} finally {
stream.close();
resStreamOut.close();
}

return jarFolder + resourceName;
}

Just change ExecutingClass to the name of your class, and call it like this:

String fullPath = ExportResource("/myresource.ext");

Edit for Java 7+ (for your convenience)

As answered by GOXR3PLUS and noted by Andy Thomas you can achieve this with:

Files.copy( InputStream in, Path target, CopyOption... options)

See GOXR3PLUS answer for more details

How to copy resources folder out of jar into program files

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 Topics



Leave a reply



Submit