Java: How to copy a folder from one jar to another? - java

Solved, see my post below
I want to copy files from one .jar to another using java. But not all files of the jar shall be copied. I only want one specific folder to be copied which also can contain subfolders. I already have the code to copy files from one jar to another:
public static void copyFiles(final File file) throws IOException {
final JarFile targetJar = new JarFile(file);
final JarOutputStream output = new JarOutputStream(new FileOutputStream(file));
final JarFile localJar = new JarFile(new File(JarEditor.class.getProtectionDomain().getCodeSource().getLocation().getFile()));
// WRAPPER_CONTENT is a String[] which contains all names of the files i want to copy
for (final String entryName : StringResource.WRAPPER_CONTENT) {
final JarEntry entryToCopy = localJar.getJarEntry(entryName);
final JarEntry targetEntry = new JarEntry(entryName);
output.putNextEntry(targetEntry);
final InputStream input = localJar.getInputStream(entryToCopy);
final byte[] buffer = new byte[10240];
int readByte = 0;
while ((readByte = input.read(buffer)) >= 0) {
output.write(buffer, 0, readByte);
}
}
output.flush();
output.close();
localJar.close();
targetJar.close();
}
Currently im copying the files by running through a hardcoded list of their names. But id prefer a more flexible way so i could add and remove resources from my jar and it still will work. They all have the same parent-folder in the jar. So how would i go through that one folder and only copy those files?
Also maybe worth mentioning, all files which shall be copied are located in the jarfile my runtime is coming from as you probably got from looking at my code.
Thanks for help
Baschdi

Bad luck i guess. I've been searching for the solution for a while now and just when i create this thread i manage to find the solution. I simply check if the name of the jarentry starts with the name of the folder i want to copy. If so, i use the same way as above to copy the jarentry to the other jar. Solved

Related

How do you create non-existing folders/subdirectories when copying a file with Java InputStream?

I have used InputStream to succesfully copy a file from one location to another:
public static void copy(File src, File dest) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("C:\\test.txt");
os = new FileOutputStream("C:\\javatest\\test.txt");
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buf)) > 0) {
os.write(buf, 0, bytesRead);
}
} finally {
is.close();
os.close();
}
}
The problem appears when I add a non-existing folder into the path, for example:
os = new FileOutputStream("C:\\javatest\\javanewfolder\\test.txt");
This returns a NullPointerException error. How can I create all of the missing directories when executing the copy process through Output Stream?
First, if possible I'd recommend you to use the java.nio.file classes (e.g. Path), instead of the File based approach. You will create Path objects by using a file system. You may use the default filesystem, if no flexibility is needed here:
final String folder = ...
final String filename = ...
final FileSystem fs = FileSystems.getDefault();
final Path myFile fs.getPath(folder, filename);
Then your problem is easily solved by a very convenient API:
final Path destinationFolder = dest.getParent();
Files.createDirectories(myPath.getParent());
try (final OutputStream os = Files.newOutputStream(myFile)) {
...
}
The Files.createDirectories() method will not fail if the directory already exists, but it may fail due to other reasons. For example if a file "foo/bar" exists, Files.createDirectories("foo/bar/folder") will most likely not succeed. ;)
Please read the javadoc carefully!
To check, if a path points to an existing directory, just user:
Files.isDirectory(somePath);
If needed, you can convert between File and Path. You will lose file system information, though:
final Path path1 = file1.toPath();
final File file2 = path2.toFile();
You could use Files.createDirectories:
Files.createDirectories(Paths.get("C:\\javatest\\javanewfolder"));
Also, you could use Files.copy to copy file )

How do you make a function that will create a .jar file in Java?

I've made some code in Java that will change some files in another .jar file, and I know that the unpacking/changing works, but the repacking doesn't. It does succeed, but when I compare the new one and the original (I removed the code that changed the files), they differed. What's interesting is that when I extracted them both into different directories, and I runned diff -rqy on them both, it didn't show any difference.
Here is the current function:
public static void add(File source, JarOutputStream target, String removeme)
throws IOException
{
BufferedInputStream in = null;
try
{
File source2 = new File(source.getPath().replaceAll("^" + removeme,
""));
// File source2 = source;
if (source.isDirectory())
{
String name = source2.getPath().replace("\\", "/");
if (!name.isEmpty())
{
if (!name.endsWith("/"))
name += "/";
JarEntry entry = new JarEntry(name);
entry.setTime(source.lastModified());
target.putNextEntry(entry);
target.closeEntry();
}
for (File nestedFile : source.listFiles())
add(nestedFile, target, removeme);
return;
}
JarEntry entry = new JarEntry(source2.getPath().replace("\\", "/"));
entry.setTime(source.lastModified());
target.putNextEntry(entry);
in = new BufferedInputStream(new FileInputStream(source));
byte[] buffer = new byte[2048];
while (true)
{
int count = in.read(buffer);
if (count == -1)
break;
target.write(buffer, 0, count);
}
target.closeEntry();
}
finally
{
if (in != null)
in.close();
}
}
I call it like this:
JarOutputStream zip = new JarOutputStream(
new FileOutputStream(JARFILE));
for (File nestedFile : new File(DIRECTORY).listFiles())
{
Utils.add(nestedFile, zip,
new File(DIRECTORY).getAbsolutePath());
}
zip.close();
Can anyone direct me on what to change in the function, or what other function I should use? The directory has subdirectories, so I need a function that will scan them.
Thanks in advance!
Edit: I don't want something using the jar command, because I don't want the user to need to install the JDK. I want something using pure Java (libraries are OK, as long as I can include them in the program).
Edit 2: I'm making a Minecraft modder (like MCPatcher and ModLoader), but when I run java -jar minecraft.jar, it gives me this: Invalid or corrupt jarfile. The correct .jar doesn't give this (just a main class error, which is supposed to happen).
I think you maybe interested in java.util.jar. This link maybe useful for you..
http://www.theserverside.com/discussions/thread.tss?thread_id=32600

How to extract a folder from JAR

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.
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.
Jar is simple ZIP file. You can use java.util.zip.* package to decompress files.
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;
}

Loading all files in a directory in a Java applet

How would one go about programatically loading all the resource files in a given directory in a JAR file for an applet? The resources will probably change several times over the lifetime of the program so I don't want to hardcode names in.
Normally I would just traverse the directory structure using File.list(), but I get permission issues when trying to do that within an applet. I also looked at using an enumeration with something line ClassLoader.getResources() but it only finds files of the same name within the JAR file.
Essentially what I want to do is (something like) this:
ClassLoader imagesURL = this.getClass().getClassLoader();
MediaTracker tracker = new MediaTracker(this);
Enumeration<URL> images = imagesURL.getResources("resources/images/image*.gif");
while (images.hasMoreElements()){
tracker.add(getImage(images.nextElement(), i);
i++;
}
I know I'm probably missing some obvious function, but I've spent hours searching through tutorials and documentation for a simple way to do this within an unsigned applet.
You can do it in two ways:
Rename your images, so you can enumerate them via some algorithm (e.g. image_1, image_2, image_3), then you can collect all resources you need in one go.
Otherwise you need to code a lot. The idea is that you have to:
determine the path to your JAR file:
private static final String ANCHOR_NAME = "some resource you know";
URL location = getClass().getClassLoader().getResource(ANCHOR_NAME);
URL jarLocation;
String protocol = location.getProtocol();
if (protocol.equalsIgnoreCase("jar"))
{
String path = location.getPath();
int index = path.lastIndexOf("!/" + ANCHOR_NAME);
if(index != -1)
jarLocation = new URL(path.substring(0, index));
}
if (protocol.equalsIgnoreCase("file"))
{
String string = location.toString();
int index = string.lastIndexOf(ANCHOR_NAME);
if(index != -1)
jarLocation = new URL(string.substring(0, index));
}
open it as java.util.jar.JarFile
JarFile jarFile = new JarFile(jarLocation);
iterate through all entries and match their name with a given mask
for (Enumeration entries = jarFile.entries(); entries.hasMoreElements();)
{
JarEntry entry = (JarEntry) entries.nextElement();
String entryPath = entry.getName();
if (entryPath.endsWith(".jpg"))
{
// do something with it
}
}
For additional code support, I will refer you to Spring PathMatchingResourcePatternResolver#doFindPathMatchingJarResources().

Creating a JAR file programmatically

I created a Jar file from my java code :
public void create() throws IOException{
FileOutputStream stream = new FileOutputStream(this.packagePath);
JarOutputStream out = new JarOutputStream(stream, new Manifest());
out.close();
//jarFile = new JarFile(new File(this.packagePath));
}
I get a META-INF directory, with a MANIFEST.MF file inside.
Now, when I want to add a file to the jar file :
public void addFile(File file) throws IOException{
//first, make sure the package already exists
if(!file.exists()){
throw new IOException("Make" +
" sure the package file already exists.you might need to call the Package.create() " +
"method first.");
}
FileOutputStream stream = new FileOutputStream(this.packagePath);
JarOutputStream out = new JarOutputStream(stream);
/*if(jarFile.getManifest()!=null){
out = new JarOutputStream(stream,jarFile.getManifest());
}else{
out=new JarOutputStream(stream);
}*/
byte buffer[] = new byte[BUFFER_SIZE];
JarEntry jarEntry = new JarEntry(file.getName());
jarEntry.setTime(file.lastModified());
out.putNextEntry(jarEntry);
//Write file to archive
FileInputStream in = new FileInputStream(file);
while (true) {
int nRead = in.read(buffer, 0, buffer.length);
if (nRead <= 0)
break;
out.write(buffer, 0, nRead);
}
in.close();
out.close();
}
when adding a file to the JAR archive using the above code, the META-INF directory with its MANIFEST.MF disappears and I get the newly added file.
I want to be able to add the file to the jar, and still get the manifest file. inside the manifest file, I want a line with the name of the newly added jar.
Thanks.
include the ant.jar in your project classpath
codeJar jar = new Jar();
//configure the jar object
jar.execute();
Look here for info about the parameters you can set.
I think you cannot add a new entry, because you cannot open jar package for "append". I think you must create a new jar file and copying entries from old, and add your entries.
FileOutputStream stream = new FileOutputStream(this.packagePath);
This line will create a new, empty file.
To edit a jar file, you will need to back up the old version and copy its entries into your new jar file.

Categories