How can Java quickly modify the file content in Jar package

Requirement background: we wrote a small program to read log files and monitor in real-time, packaged into Jar package executable files, uploaded to each server through our web main system, and then executed by calling ssh command. Before each upload, you should modify or replace the configuration file through the decompression software. This is a little troublesome. You can find a way to dynamically generate the configuration file through the program, then modify or replace the configuration file in the jar package, and finally upload it to each server for execution.    

Implementation process: at the beginning, I read a lot of articles and sorted out a tool class for operating JAR package. When I used the methods in the tool class to modify a JAR package file of about 30M, I found that it took 7 seconds, and the method of modifying JAR file is indeed a little complex (this may require professionals developing JDK to provide simple and efficient methods). I can't stand it decisively, Then I wonder if it can be realized by other methods. I came up with a scheme, that is, instead of modifying the file in the JAR through java, I used the JAR command of linux (of course, I also called the linux command through java), first decompressed the JAR package, then replaced the configuration file in the decompressed package with the dynamically generated configuration file, and finally compressed the directory into a JAR file. After testing, it takes about 4 seconds, which is really much faster. After thinking for a long time, I didn't think of any better way. When I was about to implement it, I saw this command: jar uf test.jar manifest.mf   - u update the existing JAR package (add files to the JAR package)   - f specify the JAR file name; Instant inspiration, directly call this command is not OK! Through this order, it was easy to finish in a few seconds. It was almost at the end of the mountain and water. There was no way to doubt. There was another village with a bright future.

The following is the tool class I sorted out to operate the Jar package (which has passed the test and can be used directly), including reading the file content of the Jar package, traversing the Jar package, modifying the file content of the Jar package and other operations for your reference.

  1 package com.agent.util;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.ByteArrayOutputStream;
  5 import java.io.FileNotFoundException;
  6 import java.io.FileOutputStream;
  7 import java.io.IOException;
  8 import java.io.InputStream;
  9 import java.io.InputStreamReader;
 10 import java.util.Enumeration;
 11 import java.util.Iterator;
 12 import java.util.Map;
 13 import java.util.Set;
 14 import java.util.TreeMap;
 15 import java.util.jar.JarEntry;
 16 import java.util.jar.JarFile;
 17 import java.util.jar.JarOutputStream;
 18 
 19 
 20 public class JarUtil {
 21 
 22     /**
 23      * Read all the file contents of the jar package and display the list of jar file contents
 24      * @param jarFileName
 25      * @throws IOException
 26      */
 27     public static void readJARList(String jarFilePath) throws IOException {
 28         // Create JAR file object 
 29         JarFile jarFile = new JarFile(jarFilePath);
 30         // Enumeration to obtain the entity in the JAR file, that is, the relative path
 31         Enumeration en = jarFile.entries();
 32         System.out.println("file name\t file size\t Compressed size");
 33        // Traversal displays the content information in the JAR file 
 34         while (en.hasMoreElements()) {
 35             // Call method display content 
 36             process(en.nextElement());
 37         }
 38     }
 39 
 40     // Display object information
 41     private static void process(Object obj) {
 42         // Object to Jar object
 43         JarEntry entry = (JarEntry) obj;
 44         // File name
 45         String name = entry.getName();
 46         // file size 
 47         long size = entry.getSize();
 48         // Compressed size 
 49         long compressedSize = entry.getCompressedSize();
 50         System.out.println(name + "\t" + size + "\t" + compressedSize);
 51     }
 52 
 53     /**
 54      * Read the contents of the specified file in the jar package
 55      * @param jarFileName jar Package file path
 56      * @param fileName  file name
 57      * @throws IOException
 58      */
 59     public static void readJarFile(String jarFilePath,String fileName) throws IOException{
 60         JarFile jarFile = new JarFile(jarFilePath);
 61         JarEntry entry = jarFile.getJarEntry(fileName);
 62         InputStream input = jarFile.getInputStream(entry);
 63         readFile(input);
 64         jarFile.close();
 65     }
 66 
 67 
 68     public static void readFile(InputStream input) throws IOException{
 69         InputStreamReader in = new InputStreamReader(input);
 70         BufferedReader reader = new BufferedReader(in);
 71         String line ;
 72         while((line = reader.readLine())!=null){
 73             System.out.println(line);
 74         }
 75         reader.close();
 76     }
 77 
 78     /**
 79      * Read stream
 80      *
 81      * @param inStream
 82      * @return Byte array
 83      * @throws Exception
 84      */
 85     public static byte[] readStream(InputStream inStream) throws Exception {
 86         ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
 87         byte[] buffer = new byte[1024];
 88         int len = -1;
 89         while ((len = inStream.read(buffer)) != -1) {
 90             outSteam.write(buffer, 0, len);
 91         }
 92         outSteam.close();
 93         inStream.close();
 94         return outSteam.toByteArray();
 95     }
 96 
 97     /**
 98      * Modify or add files in Jar package
 99      * @param jarFile jar Package path
100      * @param entryName File name to write
101      * @param data   File content
102      * @throws Exception
103      */
104     public static void writeJarFile(String jarFilePath,String entryName,byte[] data) throws Exception{
105 
106         //1. First, read all the contents of the original Jar package into memory and save it with TreeMap
107         JarFile  jarFile = new JarFile(jarFilePath);
108         //You can keep the order of arrangement, so use TreeMap instead of HashMap
109         TreeMap tm = new TreeMap();
110         Enumeration es = jarFile.entries();
111         while(es.hasMoreElements()){
112             JarEntry je = (JarEntry)es.nextElement();
113             byte[] b = readStream(jarFile.getInputStream(je));
114             tm.put(je.getName(),b);
115         }
116 
117         JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFilePath));
118         Iterator it = tm.entrySet().iterator();
119         boolean has = false;
120 
121         //2. Re write the TreeMap to the original jar. If there is already an entryName file in the TreeMap, overwrite it, otherwise add it at the end
122         while(it.hasNext()){
123             Map.Entry item = (Map.Entry) it.next();
124             String name = (String)item.getKey();
125             JarEntry entry = new JarEntry(name);
126             jos.putNextEntry(entry);
127             byte[] temp ;
128             if(name.equals(entryName)){
129                 //cover
130                 temp = data;
131                 has = true ;
132             }else{
133                 temp = (byte[])item.getValue();
134             }
135             jos.write(temp, 0, temp.length);
136         }
137 
138         if(!has){
139             //Finally add
140             JarEntry newEntry = new JarEntry(entryName);
141             jos.putNextEntry(newEntry);
142             jos.write(data, 0, data.length);
143         }
144         jos.finish();
145         jos.close();
146 
147     }
148 
149 
150     /**
151      * Test case
152      * @param args
153      * @throws Exception
154      */
155     public static void main(String args[]) throws Exception{
156 
157         //
158         readJarFile("D:\\esjavaclient-0.0.1-SNAPSHOT.jar","es-info.properties");
159 
160         String data = "helloBabydsafsadfasdfsdafsdgasdgweqtqwegtqwfwefasdfasfadfasf";
161 
162         long start = System.currentTimeMillis();
163 
164         writeJarFile("D:\\esjavaclient-0.0.1-SNAPSHOT.jar","es-info.properties",data.getBytes());
165 
166         long end = System.currentTimeMillis();
167 
168         System.out.println(end-start);
169 
170         readJarFile("D:\\esjavaclient-0.0.1-SNAPSHOT.jar","es-info.properties");
171 
172     }
173 
174 }

There is a test case above. The test reads one of the configuration files, then modifies the file, and finally reads it out. A 30M Jar package file takes more than 7 seconds, which is unbearable. I found many articles on the Internet, read the Zip related source code, and found a way to directly modify the contents of Jar package files. Finally, we can only find another way to find a very efficient method.

As for the tool class, there is no method to delete files. You can modify it slightly according to the modified method.

Blog for tool class reference:

http://blog.csdn.net/young_kim1/article/details/50482398

http://javasam.iteye.com/blog/1486803


How to quickly modify the file contents in Jar package:

Post the code I cleverly implemented: (if you don't know how Java calls linux commands, please fill in this knowledge first)

  1        String cmd = "jar uf esjavaclient-0.0.1-SNAPSHOT.jar config.properties";
  2         String[] cmds = {"/bin/sh","-c",cmd};
  3         Process pro;
  4         try {
  5             pro = Runtime.getRuntime().exec(cmds);
  6             pro.waitFor();
  7             InputStream in = pro.getInputStream();
  8             BufferedReader read = new BufferedReader(new InputStreamReader(in)); String line = null; while((line = read.readLine())!=null){ System.out.println(line); } } catch (Exception e) { // TODO Auto-generated catch block  e.printStackTrace(); } 

By executing these commands, we can quickly overwrite the configuration file config.properties in Jar to achieve the purpose of modification.

Conclusion: if the file in the Jar package is not large, you can use the methods provided by the tool class to operate the Jar package. If it is relatively large, I recommend using my ingenious method to modify the Jar package.

Finally: give some suggestions to professionals developing JDK. The API for operating compressed packages is not strong enough. I hope it can be improved in the future

Reproduced at: https://www.cnblogs.com/pfblog/p/7227184.html

Related resources: Used to modify the jar package path_ Java modify jar package file, jar package file path - Java

Tags: Java

Posted on Tue, 02 Nov 2021 01:55:59 -0400 by Joseph Witchard