摘要:有些时候,我们希望对某个已有的包写入新的文件或覆写已有文件如果能够像操作普通文件系统一样操作包里的文件就再好不过了,那么下面的就是这样一个工具用法很简单首先使用创建对象,参数就是你想要写入的文件位置然后直接调用就能够得到文件
有些时候,我们希望对某个已有的jar/war包写入新的文件、或覆写已有文件;
如果能够像操作普通文件系统一样操作jar/war包里的文件就再好不过了,那么下面的WarWriter.java就是这样一个工具:
package kilim.tools; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; /** * Utility to write to a jar/war file. * * @author pf_miles * */ public class WarWriter { // the war file to write at last private File warFile; // the temp directory to pre-write... private File tempDir; private static byte[] buf = new byte[1048576];// the writing buffer /** * create a war writer upon a war file... should also works for a jar file * * @param warPath * the absolute path of the underlying war file */ public WarWriter(String warPath) { File f = new File(warPath); if (!f.exists()) throw new RuntimeException("War file does not exist: " + warPath); // test if zip format JarInputStream i = null; try { i = new JarInputStream(new FileInputStream(f)); if (i.getNextEntry() == null) { throw new RuntimeException("Not jar/war format: " + warPath); } } catch (Exception e) { throw new RuntimeException("Not jar/war format: " + warPath); } finally { try { if (i != null) i.close(); } catch (IOException e) { } } this.warFile = f; // create temp directory this.tempDir = createTempDirectory(f.getName()); } private static File createTempDirectory(String warName) { final File temp; try { temp = File.createTempFile(warName, Long.toString(System.currentTimeMillis())); temp.deleteOnExit(); } catch (IOException e) { throw new RuntimeException(e); } if (!(temp.delete())) { throw new RuntimeException("Could not delete temp file: " + temp.getAbsolutePath()); } if (!(temp.mkdir())) { throw new RuntimeException("Could not create temp directory: " + temp.getAbsolutePath()); } return (temp); } /** * Complete writing, rebuild the final result jar/war file and do cleaning. * * @throws IOException */ public void done() throws IOException { // really writing to the war file, in fact a merging from the temp dir // writing to war // // listing temp dir files in jar entry naming style MaptempDirFiles = listFilesInJarEntryNamingStyle(this.tempDir, this.tempDir.getAbsolutePath()); // // create temp war File tempWar = File.createTempFile(this.warFile.getName(), null); // // merging write to the temp war JarOutputStream jos = new JarOutputStream(new FileOutputStream(tempWar)); JarFile jf = new JarFile(this.warFile); try { Enumeration iter = jf.entries(); while (iter.hasMoreElements()) { JarEntry e = iter.nextElement(); String name = e.getName(); if (!e.isDirectory() && name.endsWith(".jar")) { writeJarEntry(e, filterByDirName(tempDirFiles, name), jf, jos); } else { // prefer file in dir to war InputStream fin = null; if (tempDirFiles.containsKey(name)) { File f = tempDirFiles.get(name); if (!e.isDirectory()) fin = new FileInputStream(f); addEntry(name, fin, f.lastModified(), jos); tempDirFiles.remove(name); } else { if (!e.isDirectory()) fin = jf.getInputStream(e); addEntry(name, fin, e.getTime(), jos); } } } } finally { if (jf != null) jf.close(); } // // writing remained files in dir for (Map.Entry remain : tempDirFiles.entrySet()) { String dirFileName = remain.getKey(); File dirFile = remain.getValue(); InputStream in = null; if (!dirFile.isDirectory()) in = new FileInputStream(dirFile); addEntry(dirFileName, in, dirFile.lastModified(), jos); } // // replace the target war using the temp war jos.close(); moveTo(tempWar, warFile); // clean // // cleaning temp dir recurDel(this.tempDir); } // move from to files private void moveTo(File from, File to) throws IOException { // try rename directly if (!from.renameTo(to)) { // renameTo failed, fallback to file flowing... System.out.println("File.renameTo failed, fallback to file streaming..."); if (!from.exists()) throw new IOException("From file does not exist: " + from.getAbsolutePath()); if (!to.exists() && !to.createNewFile()) throw new IOException("To from does not exist and cannot be created: " + to.getAbsolutePath()); OutputStream o = new FileOutputStream(to); try { flowTo(new FileInputStream(from), o); } finally { from.delete(); o.close(); } System.out.println("File stream flowing moving done!"); } } /* * list the files&dirs in the specified dir, in a jar entry naming style: 1) * all file names come with no preceding "/" 2) all file names of * directories must be suffixed by a "/" */ private static Map listFilesInJarEntryNamingStyle(File f, String basePath) { Map ret = new HashMap (); String name = f.getAbsolutePath().substring(basePath.length()); if (name.startsWith("/")) name = name.substring(1); if (f.isDirectory()) { if (!name.endsWith("/")) name += "/"; for (File sub : f.listFiles()) { ret.putAll(listFilesInJarEntryNamingStyle(sub, basePath)); } } // add the current level directory itself except for the root dir if (!"/".equals(name)) ret.put(name, f); return ret; } private static void recurDel(File file) { if (file.isDirectory()) { for (File item : file.listFiles()) recurDel(item); } file.delete(); } // merging write jar entry private void writeJarEntry(JarEntry origJarEntry, Map mergingFiles, JarFile origWar, JarOutputStream targetWarStream) throws IOException { // if there"s no merging file for this jar entry, write the original jar // data directly if (mergingFiles == null || mergingFiles.isEmpty()) { JarEntry je = new JarEntry(origJarEntry.getName()); je.setTime(origJarEntry.getTime()); targetWarStream.putNextEntry(je); flowTo(origWar.getInputStream(origJarEntry), targetWarStream); targetWarStream.closeEntry(); } else { String origJarEntryName = origJarEntry.getName(); long modTime = -1; String mergingDirName = origJarEntryName + "/"; if (mergingFiles.containsKey(mergingDirName)) { modTime = mergingFiles.get(mergingDirName).lastModified(); } else { modTime = origJarEntry.getTime(); } JarEntry je = new JarEntry(origJarEntryName); je.setTime(modTime); targetWarStream.putNextEntry(je); mergingFiles.remove(mergingDirName); // build the jar data String jarSimpleName = origJarEntryName.contains("/") ? origJarEntryName.substring(origJarEntryName.lastIndexOf("/") + 1) : origJarEntryName; // // build the tmp jar file to write to File tmpOutputJarFile = File.createTempFile(jarSimpleName, null); JarOutputStream tmpOutputJar = new JarOutputStream(new FileOutputStream(tmpOutputJarFile)); // // dump the original jar file to iterate over File tmpOrigJarFile = buildTempOrigJarFile(jarSimpleName + "_orig", origWar.getInputStream(origJarEntry)); JarFile tmpOrigJar = new JarFile(tmpOrigJarFile); for (Enumeration e = tmpOrigJar.entries(); e.hasMoreElements();) { JarEntry origJarItemEntry = e.nextElement(); String origJarItemEntryName = origJarItemEntry.getName(); String mergingFileName = mergingDirName + origJarItemEntryName; InputStream itemIn = null; long itemModTime = -1; // prefer dir files to origJar entries if (mergingFiles.containsKey(mergingFileName)) { File f = mergingFiles.get(mergingFileName); if (!origJarItemEntry.isDirectory()) itemIn = new FileInputStream(f); itemModTime = f.lastModified(); mergingFiles.remove(mergingFileName); } else { if (!origJarItemEntry.isDirectory()) itemIn = tmpOrigJar.getInputStream(origJarItemEntry); itemModTime = origJarItemEntry.getTime(); } addEntry(origJarItemEntryName, itemIn, itemModTime, tmpOutputJar); } tmpOrigJar.close(); tmpOrigJarFile.delete(); // check&write remained dir files for (Map.Entry remain : mergingFiles.entrySet()) { String dirFileName = remain.getKey(); File dirFile = remain.getValue(); InputStream in = null; if (!dirFile.isDirectory()) in = new FileInputStream(dirFile); addEntry(dirFileName.substring(mergingDirName.length()), in, dirFile.lastModified(), tmpOutputJar); } tmpOutputJar.close(); // write to war InputStream jarData = new FileInputStream(tmpOutputJarFile); flowTo(jarData, targetWarStream); jarData.close(); tmpOutputJarFile.delete(); targetWarStream.closeEntry(); } } // build a temp file containing the given inputStream data private File buildTempOrigJarFile(String name, InputStream in) throws IOException { File f = File.createTempFile(name, null); OutputStream out = new FileOutputStream(f); try { flowTo(in, out); } finally { out.close(); } return f; } // data stream "flow" from in to out, pseudo-zero-copy private static void flowTo(InputStream in, OutputStream out) throws IOException { try { for (int count = in.read(buf); count != -1; count = in.read(buf)) { out.write(buf, 0, count); } } finally { in.close(); } } // collect entries which contain the specified dir path segment, and also // delete from the original map private Map filterByDirName(Map nameFileMapping, String pathSegment) { if (nameFileMapping == null || nameFileMapping.isEmpty()) return Collections.emptyMap(); Map ret = new HashMap (); if (!pathSegment.endsWith("/")) pathSegment += "/"; for (Iterator > iter = nameFileMapping.entrySet().iterator(); iter.hasNext();) { Map.Entry e = iter.next(); if (e.getKey().contains(pathSegment)) { ret.put(e.getKey(), e.getValue()); iter.remove(); } } return ret; } private static void addEntry(String entryName, InputStream in, long modTime, JarOutputStream target) throws IOException { JarEntry e = new JarEntry(entryName); e.setTime(modTime); target.putNextEntry(e); if (in != null) { flowTo(in, target); } target.closeEntry(); } /** * create outputStream writing to the specified war/jar file, all paths * specified here are relative to the root of the war/jar. */ public OutputStream getFileOutputStream(String relPath) throws IOException { if (relPath.startsWith("/")) relPath = relPath.substring(1); if (relPath.endsWith("/")) relPath = relPath.substring(0, relPath.length() - 1); File f = new File(this.tempDir.getAbsolutePath() + "/" + relPath); File p = f.getParentFile(); if (p != null && !p.exists()) { p.mkdirs(); } if (!f.exists()) f.createNewFile(); return new FileOutputStream(f); } /** * get the temporarily pre-writing directory */ public String getTempPrewriteDir() { return this.tempDir.getAbsolutePath(); } /** * return the current writing war file path */ public String getWarFilePath() { return this.warFile.getAbsolutePath(); } }
用法很简单:
首先使用WarWriter ww = new WarWriter(path_to_war_file);创建WarWriter对象,参数就是你想要写入的jar/war文件位置;
然后...直接调用ww.getFileOutputStream(relPath)就能够得到war/jar文件内部对应文件的OutputStream了,然后就可以向该stream写入数据了,就是这么简单(写完记得关闭outputStream仍然是个好习惯);
其中relPath是相对于jar/war包根目录的相对路径,比如传入WEB-INF/web.xml的话,就会得到jar/war文件内部WEB-INF/web.xml这个entry的输出流; 若该entry不存在则会自动创建;
最后,当所有的写入都完成后,记得调用ww.done();, 这会执行一些数据同步操作,并清理工作空间, 整个写入才算完成。
gist地址: https://gist.github.com/pfmiles/158a805904a98944793d
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/64435.html
摘要:基于和命名空间的声明式事务管理目前推荐的方式,其最大特点是与结合紧密,可以充分利用切点表达式的强大支持,使得管理事务更加灵活。基于的全注解方式将声明式事务管理简化到了极致。 Java面试通关手册(Java学习指南):https://github.com/Snailclimb/Java_Guide 历史回顾:可能是最漂亮的Spring事务管理详解 Spring事务管理 Spring支持两...
摘要:市长信箱邮件查询服务将应用部署到在上一章我完成了将部署到的工作和都具有能快速启动的特性因此是一对用来部署微服务的黄金搭档在计划中基于的应用也将部署到之上那我们就开始行动吧将部署到上需要执行以下步骤保证打包后的可执行能正常启动在应用中编写镜像 市长信箱邮件查询服务: 将SpringBoot应用部署到Docker 在上一章, 我完成了将ES部署到Docker的工作. SpringBoot和...
阅读 2951·2021-11-25 09:43
阅读 3327·2021-11-24 09:39
阅读 2828·2021-09-22 15:59
阅读 2174·2021-09-13 10:24
阅读 509·2019-08-29 17:02
阅读 2098·2019-08-29 13:23
阅读 3058·2019-08-29 13:06
阅读 3539·2019-08-29 13:04