0

I've searched high and low for a way to do this, finding all manner of different ways to approach this problem, and yet none of them have worked for me.

I simply need a way to append files to a Jar File, overwriting if they already exist, and I want the fastest way to do this too. I've tried converting it to a Zip File and adding the files using Zip File System but I run into errors such as "zip END header not found", other methods work but are painfully slow (6 MB of files took 3 minutes).

What is the fastest way to append files to a Jar File?

Edit: Yeah go ahead, downvote instead of answering, very productive.

4
  • 1
    Could you specify what methods you have tried that take too long? Commented Jul 31, 2017 at 18:16
  • I can't recall exactly, but I used a file stream to extract the contents of the Jar File. I think it was similar to the top answer to this stackoverflow.com/questions/1529611/… I then added files using some other methods and renamed the whole thing to .jar Commented Jul 31, 2017 at 18:21
  • You can use any ZIP processing library, there are tons of. I would go for truezip Commented Jul 31, 2017 at 18:34
  • Yeah but that means I still need to get the contents out of the Jar File and add my own files to it right? I want a way to do all of this quickly, hence why it would be nice if I didn't have to unpack, add files, repack and rename and could instead do it in fewer steps. Commented Jul 31, 2017 at 18:50

2 Answers 2

2

You'd do this the same way you update a text file:

  • Open an input stream from current file.
  • Open an output stream to new (temporary) file.
  • Read from input stream:
    • If no change needed, write to output stream.
    • If content should be removed, don't write it.
    • If content should be changed, change it, then write it.
    • If new content should be added at this point, write it now.
    • Repeat until all content processed.
  • Close both streams.
  • Delete/rename old file and rename new file to old file.

In your case, that means:

  • Start with list of files to be added.
  • Open ZipInputStream on current Jar file.
  • Open ZipOutputStream on new (temporary) Jar file.
  • Copy all existing entries (files), except files that will be replaced.
  • Insert all new files.
  • Close both streams.
  • Delete/rename old Jar file and rename new file to old Jar file.

You can of course flip the order.

  • Open ZipOutputStream on new (temporary) Jar file.
  • Insert all new files, and remember their names.
  • Open ZipInputStream on current Jar file.
  • Copy all existing entries (files), except files already added.
  • Close both streams.
  • Delete/rename old Jar file and rename new file to old Jar file.
Sign up to request clarification or add additional context in comments.

8 Comments

How would I go about copying the existing files except the ones that will be replaced? Streams copy stuff in bytes (from my understanding), so how do I recognise when a whole file has been copied and get its name? @Andreas
Suspicions have been confirmed: stackoverflow.com/questions/4930111/… I can't copy all the files except for the replaced ones using streams.
@QuantumVertex What does that link have to do with anything? Did you even look at how ZipInputStream works? Perhaps if you read the documentation of it, you'd realize that what I described is entirely possible, since the file names of zip entries are given to you.
I see. Will give it a shot.
This seems like a very long and complicated way of copying files, judging by the SO questions I've been looking at, is there no simpler way of doing it? @Andreas
|
0

Here is the best way I could found:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

public class JarUtil {

    public static void updateJarFile(File srcJarFile, boolean update, File ...filesToAdd) throws IOException {

        File tmpJarFile = File.createTempFile("tempJar", ".tmp");
        JarFile jarFile = new JarFile(srcJarFile);
        boolean jarUpdated = false;
        List<String> fileNames = new ArrayList<String>();

        try {
            JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(tmpJarFile));
            try {
                // Added the new files to the jar.
                for (int i = 0; i < filesToAdd.length; i++) {
                    File file = filesToAdd[i];
                    FileInputStream fis = new FileInputStream(file);
                    try {
                        byte[] buffer = new byte[1024];
                        int bytesRead = 0;
                        JarEntry entry = new JarEntry(file.getName());
                        fileNames.add(entry.getName());
                        tempJarOutputStream.putNextEntry(entry);
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            tempJarOutputStream.write(buffer, 0, bytesRead);
                        }

                        // System.out.println(entry.getName() + " added.");
                    } finally {
                        fis.close();
                    }
                }

                // Copy original jar file to the temporary one.
                Enumeration<?> jarEntries = jarFile.entries();
                while (jarEntries.hasMoreElements()) {
                    JarEntry entry = (JarEntry) jarEntries.nextElement();
                    /*
                     * Ignore classes from the original jar which are being
                     * replaced
                     */
                    String[] fileNameArray = (String[]) fileNames
                            .toArray(new String[0]);
                    Arrays.sort(fileNameArray);// required for binary search
                    if (Arrays.binarySearch(fileNameArray, entry.getName()) < 0) {
                        InputStream entryInputStream = jarFile
                                .getInputStream(entry);
                        tempJarOutputStream.putNextEntry(entry);
                        byte[] buffer = new byte[1024];
                        int bytesRead = 0;
                        while ((bytesRead = entryInputStream.read(buffer)) != -1) {
                            tempJarOutputStream.write(buffer, 0, bytesRead);
                        }
                    } else if (!update) {
                        throw new IOException(
                                "Jar Update Aborted: Entry "
                                        + entry.getName()
                                        + " could not be added to the jar"
                                        + " file because it already exists and the update parameter was false");
                    }
                }

                jarUpdated = true;
            } catch (Exception ex) {
                System.err.println("Unable to update jar file");
                tempJarOutputStream.putNextEntry(new JarEntry("stub"));
            } finally {
                tempJarOutputStream.close();
            }

        } finally {
            jarFile.close();
            // System.out.println(srcJarFile.getAbsolutePath() + " closed.");

            if (!jarUpdated) {
                tmpJarFile.delete();
            }
        }

        if (jarUpdated) {
            srcJarFile.delete();
            tmpJarFile.renameTo(srcJarFile);
            // System.out.println(srcJarFile.getAbsolutePath() + " updated.");
        }
    }
}

Original Source (modified): https://subversivebytes.wordpress.com/2012/10/11/java-programmatically-update-jar-file/ Hope this helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.