How to Copy File Inside Jar to Outside the Jar

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 files out of the currently running jar

Since your dlls are bundeled inside your jar file you could just try to acasses them as resources using ClassLoader#getResourceAsStream and write them as binary files any where you want on the hard drive.

Here is some sample code:

InputStream ddlStream = <SomeClassInsideTheSameJar>.class
.getClassLoader().getResourceAsStream("some/pack/age/somelib.dll");

try (FileOutputStream fos = new FileOutputStream("somelib.dll");){
byte[] buf = new byte[2048];
int r;
while(-1 != (r = ddlStream.read(buf))) {
fos.write(buf, 0, r);
}
}

The code above will extract the dll located in the package some.pack.age to the current working directory.

Copying images from JAR file to a folder outside

Probably the simplest way to do it is like this:

public static void main(String[] args) throws URISyntaxException, IOException {
File imagesCopy = new File("C:\\Users\\<YOURNAMEHERE>\\images");

URI uri = ImageCopy.class.getResource("/images").toURI();
if (!uri.toString().startsWith("file:")) {
Map<String, String> env = new HashMap<>();
env.put("create", "true");
FileSystems.newFileSystem(uri, env);
}
Path imagesOrg = Paths.get(uri);
System.out.println(imagesOrg);

if (!imagesCopy.exists()) {
imagesCopy.mkdir();
try(DirectoryStream<Path> paths = Files.newDirectoryStream(imagesOrg)) {
for (final Path child : paths) {
System.out.println(child);
try {
String targetPath = imagesCopy.getAbsolutePath() + File.separator + child.getFileName().toString();
System.out.println(targetPath);
Files.copy(child, Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

It's not super-pretty, but it works. Might need to fiddle with the code if you have nested directories.

Note that you must create the FileSystem before accessing it (as per the Oracle Docs). I don't know why this is required, but there we go.

I've tested this and it will copy files from inside your JAR to wherever you would like.

Use FileChannel or Files.copy to copy file inside jar

You're barking up the wrong tree. Files.copy has nothing whatsoever to do with support (or lack thereof) of chinese characters, and java does support full unicode pathnames. Yes, it's clear your code isn't currently working as designed, and it can be fixed, but the problem isn't Files.copy.

Sidenote: Your code is broken

Main.class.getResourceAsStream is the correct way to pull resources from your codebase, however, this is a resource, and therefore, you must close it. Wrap it in a try block, that's the smart way to do it.

Objects.requireNonNull should not be used here - its purpose is to forcibly throw a NullPointerException. That's all it does. This code will already throw an NPE if somehow that resource is missing. This means the requireNonNull is completely pointless (it is enforcing a thing that already happens), and if you want clean code, either is inappropriate: You should be rethrowing with an exception that properly conveys the notion that the app is broken.

However, that isn't a good idea either: We don't throw exceptions for bugs or broken deployments. If you think you should, then you should wrap every line of code in your entire java project with a try/catch block, after all, evidently we can't assume anything. We can't even assume java.lang.String is available at run time - clearly this is not a sustainable point of view. In other words, you may safely assume that the resource couldn't possibly not be there for the purposes of exception flow.

Thus, we get to this much simpler and safer code:

try (var in = Main.class.getResourceAsStream("/amres/core/template.xlsx")) {
Files.copy(in, Paths.get("C:/我的/test.xlsx"), StandardCopyOption.REPLACE_EXISTING);
}

Note that in general, catching an exception and handling it with e.printStackTrace() is also just bad, in all cases: You're printing the exception to a place it probably shouldn't go, tossing useful info such as the causal chain, and then letting code exception continue even though your code's state is clearly in an unexpected and therefore unknown state. The best general solution is to actually throws the exception onwards. If that is not feasible or you just don't want to care about it right now and thus relying on your IDE's auto-fixes and not bothering to edit anything, then at least fix your IDE's auto-fixer to emit non-idiotic code. throw new RuntimeException("uncaught", e) is the proper 'I do not want to care about this right now' fillin. So, fix your IDE. It's generally in the settings, under 'template'.

What could be causing this

Every single time chars turn to bytes or vice versa, charset encoding is involved. Filenames look like characters and certainly when you write code, you're writing characters, and when you see the text of the NoSuchFileException, that's characters - but what about all the stuff in between? In addition, names in file systems themselves are unclear: Some filesystem names are byte-based. For example, Apple's APFS is entirely bytebased. File names just are bytes, and the idea of rendering these bytes onto the screen (and translating e.g. touch foobar.txt on the command line onto the byte sequence value for the file name) are done with UTF-8 merely by convention. In contrast some file systems encode this notion of a set encoding directly into its APIs. Best bet is to UTF_8 all the things, that's the least chance of things going awry.

So, let's go through the steps:

  1. You write java code in a text editor. You write characters.
  2. File content is, universally, byte based, on all file systems. Therefore, when you hit the 'save' shortcut in your text editor, your characters are converted into bytes. Check that your editor is configured in UTF-8 mode. Alternatively, use backslash-u escapes to avoid the issue.
  3. You compile this code. Probably with javac, ecj, or something based on one of those two. They read in a file (so, bytes), but parse the input as characters, therefore conversion is happening. Ensure that javac/ecj is invoked with the --encoding UTF-8 parameter. If using a build tool such as maven or gradle, ensure this is explicitly configured.
  4. That code is run and prints its error to a console. A console shows it to you. The console is converting bytes (because app output is a byte-based stream) to chars in order to show it to you. Is it configured to do this using UTF-8? Check the terminal app's settings.

Check all the bolded items, 95%+ chance you'll fix your problem by doing this.

How do I copy a text file from a jar into a file outside of the jar?

Assuming said jar is on your classpath:

URL url = getClassLoader().getResource("com/test/io/test.txt");
FileOutputStream output = new FileOutputStream("test.txt");
InputStream input = url.openStream();
byte [] buffer = new byte[4096];
int bytesRead = input.read(buffer);
while (bytesRead != -1) {
output.write(buffer, 0, bytesRead);
bytesRead = input.read(buffer);
}
output.close();
input.close();

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
}
}

}`

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);
}
}
}

How to copy folder's out of resources from jar both in runtime and dev-env?

You cannot list resources in a jar.

Any workarounds you’re thinking of, are unreliable.

  • Never call the getFile() method of URL. It does not return a valid file name; it just returns the path and query portions of the URL, with any percent-escapes intact. Furthermore, jar entries are not file: URLs, so a resource path can never be a valid file name when it refers to a jar entry.
  • The only way to list things in a jar file is by iterating through all jar entries, but you aren’t even guaranteed to have access to your jar, because ClassLoaders are not guaranteed to be URLClassLoaders, and in general are not guaranteed to use jar: URLs.
  • You can’t even rely on MyApplication.class.getProtectionDomain().getCodeSource(), because getCodeSource() can return null.

If you want to copy multiple files from your jar, here are some reliable ways to do it:

  • Hard code the list of resources you plan to copy. It’s your application, so you know what files you’re putting in the jar.
  • Keep a single text file in your jar which contains a list of resource paths to copy.
  • Store your resources in a single zip archive which is embedded in your jar, and extract it yourself with a ZipInputStream which wraps MyApplication.class.getResourceAsStream.


Related Topics



Leave a reply



Submit