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
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/