摘要:有些時候,我們希望對某個已有的包寫入新的文件或覆寫已有文件如果能夠像操作普通文件系統一樣操作包里的文件就再好不過了,那么下面的就是這樣一個工具用法很簡單首先使用創建對象,參數就是你想要寫入的文件位置然后直接調用就能夠得到文件
有些時候,我們希望對某個已有的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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64435.html
摘要:基于和命名空間的聲明式事務管理目前推薦的方式,其最大特點是與結合緊密,可以充分利用切點表達式的強大支持,使得管理事務更加靈活。基于的全注解方式將聲明式事務管理簡化到了極致。 Java面試通關手冊(Java學習指南):https://github.com/Snailclimb/Java_Guide 歷史回顧:可能是最漂亮的Spring事務管理詳解 Spring事務管理 Spring支持兩...
摘要:市長信箱郵件查詢服務將應用部署到在上一章我完成了將部署到的工作和都具有能快速啟動的特性因此是一對用來部署微服務的黃金搭檔在計劃中基于的應用也將部署到之上那我們就開始行動吧將部署到上需要執行以下步驟保證打包后的可執行能正常啟動在應用中編寫鏡像 市長信箱郵件查詢服務: 將SpringBoot應用部署到Docker 在上一章, 我完成了將ES部署到Docker的工作. SpringBoot和...
閱讀 2955·2021-11-25 09:43
閱讀 3330·2021-11-24 09:39
閱讀 2837·2021-09-22 15:59
閱讀 2185·2021-09-13 10:24
閱讀 514·2019-08-29 17:02
閱讀 2105·2019-08-29 13:23
閱讀 3066·2019-08-29 13:06
閱讀 3543·2019-08-29 13:04