Java如何快速修改Jar包里的文件內容


需求背景:寫了一個實時讀取日志文件以及監控的小程序,打包成了Jar包可執行文件,通過我們的web主系統上傳到各個服務器,然后調用ssh命令執行。每次上傳前都要通過解壓縮軟件修改或者替換里面的配置文件,這樣感覺有點麻煩,就想辦法能不能通過程序動態生成配置文件,然后修改或者替換掉Jar包里的配置文件,最后再上傳到各個服務器去執行。   

實現歷程:剛開始看了大量文章,整理出來了一個操作Jar包的工具類,用工具類里面的方法去修改一個30M左右的Jar包文件時,發現耗時竟然要7秒,而且修改Jar文件的方法確實有點復雜(這個可能需要開發JDK的專業人士提供簡單、高效的方法了),果斷忍受不了,然后想能不能通過其他的方法來實現。想到了一個方案,就是不通過java去修改Jar里的文件,而是用linux的Jar命令(當然也是通過java去調用linux命令),先解壓縮jar包,然后將動態生成的配置文件替換解壓縮包里的配置文件,最后再將目錄壓縮成Jar文件。經過測試,這樣大概需要4秒的時間,確實快了不少。想了很久也沒有想到其他更好的辦法,正打算實施的時候,看到了這條命令:jar uf test.jar manifest.mf  -u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中)  -f 指定 JAR 文件名;瞬間來了靈感,直接調用這條命令不就OK了!通過這條命令幾秒鍾就輕輕松松搞定了,簡直是山窮水盡疑無路,柳暗花明又一村。

下面是我整理的操作Jar包的工具類(已經通過測試,可以拿去直接用),包括讀取Jar包的文件內容,遍歷 Jar包,修改 Jar包文件內容等操作,供大家參考。

  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      * 讀取jar包所有的文件內容,顯示JAR文件內容列表
 24      * @param jarFileName
 25      * @throws IOException
 26      */
 27     public static void readJARList(String jarFilePath) throws IOException {
 28         // 創建JAR文件對象 
 29         JarFile jarFile = new JarFile(jarFilePath);
 30         // 枚舉獲得JAR文件內的實體,即相對路徑
 31         Enumeration en = jarFile.entries();
 32         System.out.println("文件名\t文件大小\t壓縮后的大小");
 33        // 遍歷顯示JAR文件中的內容信息 
 34         while (en.hasMoreElements()) {
 35             // 調用方法顯示內容 
 36             process(en.nextElement());
 37         }
 38     }
 39 
 40     // 顯示對象信息
 41     private static void process(Object obj) {
 42         // 對象轉化成Jar對象
 43         JarEntry entry = (JarEntry) obj;
 44         // 文件名稱
 45         String name = entry.getName();
 46         // 文件大小 
 47         long size = entry.getSize();
 48         // 壓縮后的大小 
 49         long compressedSize = entry.getCompressedSize();
 50         System.out.println(name + "\t" + size + "\t" + compressedSize);
 51     }
 52 
 53     /**
 54      * 讀取jar包里面指定文件的內容
 55      * @param jarFileName jar包文件路徑
 56      * @param fileName  文件名
 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      * 讀取流
 80      *
 81      * @param inStream
 82      * @return 字節數組
 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      * 修改Jar包里的文件或者添加文件
 99      * @param jarFile jar包路徑
100      * @param entryName 要寫的文件名
101      * @param data   文件內容
102      * @throws Exception
103      */
104     public static void writeJarFile(String jarFilePath,String entryName,byte[] data) throws Exception{
105 
106         //1、首先將原Jar包里的所有內容讀取到內存里,用TreeMap保存
107         JarFile  jarFile = new JarFile(jarFilePath);
108         //可以保持排列的順序,所以用TreeMap 而不用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、將TreeMap重新寫到原jar里,如果TreeMap里已經有entryName文件那么覆蓋,否則在最后添加
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                 //覆蓋
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             //最后添加
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      * 測試案例
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 }

上面有個測試案例,測試讀取其中一個配置文件,然后修改這個文件,最后再讀取出來。 一個30M左右的Jar包文件耗時7秒多,實在忍受不了。網上找了很多文章,同時看了Zip相關的源碼,也木有找到可以直接修改Jar包文件內容的方法。 最后只能另辟蹊徑,找到了一個執行效率非常快的方法。

關於工具類沒有刪除文件的方法,大家根據修改的方法稍微改造一下即可。

工具類參考的博客:

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

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


如何快速修改Jar包里的文件內容:

貼出來我巧妙實現的代碼:(如果不知道Java 如何調用linux命令的同學,請先補習一下這方面的知識)

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

通過執行這條條命令,就可以很快將我們生成的配置文件config.properties覆蓋掉Jar里的文件,從而達到修改的目的。

總結:如果Jar包里的文件不大的話,完全可以用工具類提供的方法去操作Jar包。比較大的話,修改Jar包的方法,推薦用我那個巧妙的方法。

最后:給開發JDK的專業人士提點建議,關於操作壓縮包這方面的API還不夠強大,希望后續可以完善一下

轉載於:https://www.cnblogs.com/pfblog/p/7227184.html

相關資源:用於修改jar文件包路徑_java修改jar包文件,jar包文件路徑-Java...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM