前段時間,因為各種原因,自己動手寫了一個小的備份工具,用了一個星期,想想把它的設計思路放上來,當是筆記吧。
需求場景:這個工具起初的目的是為了解決朋友公司對其網絡的限制(不可以用任何同步軟件,git,外網SVN,U盤只讀)。本來只是想做一個自動打包和發送郵件的工具,后來就發展成了這個。
軟件功能:這個軟件最終實現的功能包括1、讀取配置文件,對配置文件中指定目錄的文件進行日期檢測,獲取對應修改過的文件。2、將讀出的文件進行壓縮;3、將壓縮文件發送到指定郵箱;4、對壓縮文件進行歷史版本的保留
根據最后的功能需求,工具最后選擇采用的API包如下:
1、 使用commons-config實現配置文件的讀寫
2、 使用commons-io實現文件的檢測和讀寫
3、 使用zip4j實現對文件的壓縮
4、 使用javamail實現郵件的發送
系統最后的流程圖如下:
主要的工具類如下:
1 //用於發送郵件的Bean 2 package com.sean.bean; 3 4 import java.io.File; 5 import java.io.IOException; 6 import java.io.InputStreamReader; 7 import java.io.UnsupportedEncodingException; 8 import java.util.Properties; 9 10 import javax.activation.DataHandler; 11 import javax.activation.FileDataSource; 12 import javax.mail.Address; 13 import javax.mail.BodyPart; 14 import javax.mail.Message; 15 import javax.mail.Message.RecipientType; 16 import javax.mail.MessagingException; 17 import javax.mail.NoSuchProviderException; 18 import javax.mail.Session; 19 import javax.mail.Transport; 20 import javax.mail.internet.AddressException; 21 import javax.mail.internet.InternetAddress; 22 import javax.mail.internet.MimeBodyPart; 23 import javax.mail.internet.MimeMessage; 24 import javax.mail.internet.MimeMultipart; 25 /** 26 * 郵件類 27 * @author Sean 28 * @blog http://www.cnblogs.com/Seanit/ 29 * @email hxy9104@126.com 30 * 2015-6-9 31 */ 32 public class EmailBean { 33 private Session session; 34 private MimeMessage message; 35 private Properties properties; 36 private MimeMultipart body; 37 38 /** 39 * 默認構造函數 40 */ 41 public EmailBean(){ 42 } 43 44 45 public EmailBean(Properties properties){ 46 this.properties=properties; 47 createMail(properties); 48 } 49 50 public EmailBean(String src){ 51 this.properties=new Properties(); 52 try { 53 properties.load(new InputStreamReader(ClassLoader.getSystemResourceAsStream(src),"utf-8")); 54 } catch (UnsupportedEncodingException e) { 55 // TODO Auto-generated catch block 56 e.printStackTrace(); 57 } catch (IOException e) { 58 // TODO Auto-generated catch block 59 e.printStackTrace(); 60 } 61 createMail(properties); 62 } 63 64 /** 65 * 創建連接 66 * @author Sean 67 * 2015-6-8 68 * @param properties 郵箱相關配置 69 */ 70 public void createMail(Properties properties) { 71 this.properties=properties; 72 try { 73 session=Session.getDefaultInstance(properties,null); 74 message=new MimeMessage(session); 75 body=new MimeMultipart(); 76 } catch (Exception e) { 77 e.printStackTrace(); 78 throw new RuntimeException("郵件初始化失敗"); 79 } 80 } 81 82 /** 83 * 設置發件人 84 * @author Sean 85 * 2015-6-8 86 * @param from 發件人地址 87 * @return 成功返回true,失敗防護false 88 */ 89 public boolean setFrom(String from){ 90 try { 91 message.setFrom(new InternetAddress(from)); 92 return true; 93 } catch (Exception e) { 94 e.printStackTrace(); 95 return false; 96 } 97 } 98 99 /** 100 * 設置收件人 101 * @author Sean 102 * 2015-6-8 103 * @param toSend 收件人郵箱地址 104 * @return 成功返回true,失敗防護false 105 */ 106 public boolean setTOSend(String toSend){ 107 try { 108 message.setRecipients(RecipientType.TO, toSend); 109 return true; 110 } catch (MessagingException e) { 111 e.printStackTrace(); 112 return false; 113 } 114 } 115 116 /** 117 * 設置抄送人 118 * @author Sean 119 * 2015-6-8 120 * @param copyTo 抄送人地址 121 * @return 成功返回true,失敗防護false 122 */ 123 public boolean setCopyTO(String copyTo){ 124 try { 125 message.setRecipients(RecipientType.CC, (Address[])InternetAddress.parse(copyTo)); 126 return true; 127 } catch (Exception e) { 128 e.printStackTrace(); 129 return false; 130 } 131 } 132 133 134 /** 135 * 設置主題 136 * @author Sean 137 * 2015-6-8 138 * @param subject 郵件主題 139 * @return 成功返回true,失敗防護false 140 */ 141 public boolean setSubject(String subject){ 142 try { 143 message.setSubject(subject); 144 return true; 145 } catch (MessagingException e) { 146 e.printStackTrace(); 147 return false; 148 } 149 } 150 151 /** 152 * 設置郵件正文 153 * @author Sean 154 * 2015-6-8 155 * @param content 郵件正文 156 * @return 成功返回true,失敗防護false 157 */ 158 public boolean setContent(String content){ 159 try { 160 BodyPart bodyPart=new MimeBodyPart(); 161 bodyPart.setContent(""+content, "text/html;charset=GBK"); 162 body.addBodyPart(bodyPart); 163 return true; 164 } catch (Exception e) { 165 e.printStackTrace(); 166 return false; 167 } 168 } 169 170 /** 171 * 添加附件 172 * @author Sean 173 * 2015-6-8 174 * @param src 附件地址 175 * @return 176 */ 177 public boolean addFiles(String src){ 178 try { 179 BodyPart bodyPart=new MimeBodyPart(); 180 FileDataSource file =new FileDataSource(src); 181 bodyPart.setDataHandler(new DataHandler(file)); 182 bodyPart.setFileName(file.getName()); 183 body.addBodyPart(bodyPart); 184 return true; 185 } catch (Exception e) { 186 e.printStackTrace(); 187 return false; 188 } 189 } 190 191 /** 192 * 發送郵件 193 * @author Sean 194 * 2015-6-8 195 * @return 196 */ 197 public boolean send(){ 198 Transport transport=null; 199 try { 200 message.setContent(body); 201 message.saveChanges(); 202 transport=session.getTransport(); 203 transport.connect(properties.getProperty("mail.smtp.host"), properties.getProperty("username"), properties.getProperty("password")); 204 transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO)); 205 if(message.getRecipients(Message.RecipientType.CC)!=null){ 206 transport.sendMessage(message, message.getRecipients(Message.RecipientType.CC)); 207 } 208 return true; 209 } catch (Exception e) { 210 e.printStackTrace(); 211 return false; 212 }finally{ 213 try { 214 transport.close(); 215 } catch (MessagingException e) { 216 // TODO Auto-generated catch block 217 e.printStackTrace(); 218 } 219 } 220 } 221 }
1 //用於操作配置文件的bean 2 package com.sean.bean; 3 4 import org.apache.commons.configuration.ConfigurationException; 5 import org.apache.commons.configuration.PropertiesConfiguration; 6 /** 7 * 配置文件操作類 8 * 實現功能:對properties 文件進行讀寫操作 9 * @author Sean 10 * 2015-6-6 11 */ 12 public class PropertiesBean { 13 private String src=""; 14 private PropertiesConfiguration pc=null; 15 /** 16 * 默認構造函數 17 */ 18 public PropertiesBean() {} 19 public PropertiesBean(String src) { 20 this.src=src; 21 init(); 22 } 23 24 /** 25 * 初始化函數 26 * @author Sean 27 * 2015-6-6 28 */ 29 public void init(){ 30 if(src.trim().equals("")){ 31 throw new RuntimeException("The path is null"); 32 } 33 try { 34 pc=new PropertiesConfiguration(src); 35 } catch (ConfigurationException e) { 36 e.printStackTrace(); 37 } 38 } 39 40 /** 41 * 取值函數,根據對應的關鍵字獲取對應的值 42 * @author Sean 43 * 2015-6-6 44 * @param key 關鍵字 45 * @return 返回關鍵字對應的值,若無該關鍵字,拋出異常 46 */ 47 public String getValue(String key){ 48 if(!pc.containsKey(key)){ 49 throw new RuntimeException("not such a key"); 50 } 51 return pc.getString(key); 52 } 53 54 /** 55 * 設置對應的值,傳入鍵值對,根據關鍵字修改對應的值 56 * @author Sean 57 * 2015-6-6 58 * @param key 關鍵字 59 * @param value 值 60 */ 61 public void setValue(String key,String value){ 62 if(!pc.containsKey(key)){ 63 throw new RuntimeException("not such a key"); 64 } 65 pc.setProperty(key, value); 66 try { 67 pc.save(); 68 } catch (ConfigurationException e) { 69 // TODO Auto-generated catch block 70 e.printStackTrace(); 71 } 72 } 73 74 /** 75 * 設置配置文件的地址 76 * @author Sean 77 * 2015-6-6 78 * @param src 配置文件的路徑 79 */ 80 public void setSrc(String src) { 81 this.src = src; 82 } 83 }
//用於進行IO操作的Bean package com.sean.bean; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.commons.io.FileUtils; /** * 文件操作工具類 * @author Sean * @blog http://www.cnblogs.com/Seanit/ * @email hxy9104@126.com * 2015-6-6 */ public class FilesBean { private List<File> list=null; public FilesBean(){} /** * 獲取文件夾目錄下的列表,目錄為空返回空列表 * @param dir 傳入對應文件夾目錄 * @param ifFirst 控制遞歸時候的循環表示 * @return 返回list<File>文件列表 */ public List<File> listFiles(String dir,boolean ifFirst){ /** * 遞歸判斷,僅在第一次調用函數的時候新建列表,為防止重復存入列表 */ if(ifFirst){ list=new ArrayList<File>(); } if (dir==null||dir=="") { return null; } for (File f : FileUtils.getFile(dir + "\\").listFiles()) { if (f.isDirectory()) { listFiles(f.getAbsolutePath(),false); } else { list.add(f); } } return list; } /** * 根據過濾條件獲取列表 * @param dir 目錄 * @param filter 過濾條件 * @param ifFirst 遞歸標識 * @return 返回文件列表 */ public List<File> listFiles(String dir,String filter,boolean ifFirst){ /** * 遞歸判斷,僅在第一次調用函數的時候新建列表,為防止重復存入列表 */ if(ifFirst){ list=new ArrayList<File>(); } if (dir==null||dir=="") { return null; } for (File f : FileUtils.getFile(dir + "\\").listFiles()) { if (f.isDirectory()) { listFiles(f.getAbsolutePath(),filter,false); } else { if(!f.getName().endsWith(filter)){ list.add(f); } } } return list; } /** * 獲取指定目錄下,指定日期修改后的文件 * @param dir 傳入指定檢測目錄 * @param date 傳入指定修改日期 * @param ifFirst 控制遞歸時候的循環表示 * @return 返回獲取的文件列表,若目錄或日期為空,則返回null */ public List<File> getModifiedFiles(String dir,Date date,boolean ifFirst){ /** * 遞歸判斷,僅在第一次調用函數的時候新建列表,為防止重復存入列表 */ if(ifFirst){ list=new ArrayList<File>(); } if (dir==null||dir==""||date==null) { return null; } for (File f : FileUtils.getFile(dir + "\\").listFiles()) { if (f.isDirectory()) { getModifiedFiles(f.getAbsolutePath(),date,false); } else { if (FileUtils.isFileNewer(f, date.getTime())) { list.add(f); } } } return list; } /** * 獲取某一日期之前創建的文件 * @author Sean * 2015-6-9 * @param dir 文件路徑 * @param ifFirst 控制遞歸時候的循環表示 * @param date 日期 * @return 返回列表 */ public List<File> getOlderFiles(String dir,Date date,boolean ifFirst){ /** * 遞歸判斷,僅在第一次調用函數的時候新建列表,為防止重復存入列表 */ if(ifFirst){ list=new ArrayList<File>(); } if (dir==null||dir==""||date==null) { return null; } for (File f : FileUtils.getFile(dir + "\\").listFiles()) { if (f.isDirectory()) { getOlderFiles(f.getAbsolutePath(),date,false); } else { if (FileUtils.isFileOlder(f, date)) { list.add(f); } } } return list; } /** * 獲取除過濾條件外的修改文件列表 * @param dir * @param date * @param filter * @param ifFirst * @return */ public List<File> getModifiedFiles(String dir,Date date,String filter,boolean ifFirst){ /** * 遞歸判斷,僅在第一次調用函數的時候新建列表,為防止重復存入列表 */ if(ifFirst){ list=new ArrayList<File>(); } if (dir==null||dir==""||date==null) { return null; } for (File f : FileUtils.getFile(dir + "\\").listFiles()) { if (f.isDirectory()) { getModifiedFiles(f.getAbsolutePath(),date,filter,false); } else { if (FileUtils.isFileNewer(f, date.getTime())) { if(!f.getName().endsWith(filter)){ list.add(f); } } } } return list; } }
//用於壓縮文件的工具類 package com.sean.utils; import java.io.File; import java.util.ArrayList; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.util.Zip4jConstants; public class ZipUtil { /** * 壓縮文件列表,無目錄結構 * @author Sean * 2015-6-6 * @param zipSrc zip文件地址 * @param list 文件列表 * @return 若成功,返回文件地址,不成功返回null */ public static String zipFiles(String zipSrc,ArrayList<File> list){ ZipParameters zipParameters=new ZipParameters(); zipParameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); zipParameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_MAXIMUM); ZipFile zipFile=null; try { zipFile=new ZipFile(zipSrc); } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } try { zipFile.addFiles(list, zipParameters); } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } return zipSrc; } /** * 壓縮文件夾 * @author Sean * 2015-6-6 * @param src 文件夾地址 * @param zipSrc 壓縮文件存放地址 * @return 若成功,返回文件夾地址,若不成功,返回null */ public static String zipFolder(String src,String zipSrc){ ZipParameters zipParameters=new ZipParameters(); zipParameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); zipParameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_MAXIMUM); ZipFile zipFile=null; try { zipFile=new ZipFile(zipSrc); } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } try { zipFile.addFolder(src, zipParameters); } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } return zipSrc; } /** * 往zip文件中添加文件列表 * @author Sean * 2015-6-7 * @param zipSrc zip文件路徑 * @param list 要添加的文件列表 * @param src 要添加到zip文件中的哪個路徑 */ public static void addFileToZip(String zipSrc,ArrayList<File> list,String src){ ZipFile zipFile=null; try { zipFile=new ZipFile(zipSrc); } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); } ZipParameters zipParameters=new ZipParameters(); zipParameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); zipParameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); zipParameters.setRootFolderInZip(src); try { zipFile.addFiles(list, zipParameters); } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 添加文件列表到zip根目錄下 * @author Sean * 2015-6-7 * @param zipSrc zip文件地址 * @param list 添加的文件列表 */ public static void addFileToZip(String zipSrc,ArrayList<File> list){ addFileToZip(zipSrc,list,""); } }
主流程: package com.sean.main; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import com.sean.bean.EmailBean; import com.sean.bean.FilesBean; import com.sean.bean.PropertiesBean; import com.sean.utils.ZipUtil; /** * change2Mail 主程序 * 檢測文件修改情況並發送給指定郵箱 * @version 1.0 * @author sean * 2015-06-09 * */ public class Change2Mail { /** * @param args * @throws ParseException */ public static void main(String[] args) throws ParseException{ //獲取properties操作類 PropertiesBean propertiesBean = new PropertiesBean("cfg.properties"); FilesBean filesBean=new FilesBean(); //郵件對象 EmailBean email=new EmailBean("cfg.properties"); String first = propertiesBean.getValue("first"); //當前日期 String today=new SimpleDateFormat("yyyyMMdd").format(new Date()); //郵件正文 String content=""; //首次使用判斷 if (first.trim().equals("true")) { System.out.println("首次運行"); content+="歡迎首次使用change2Mail,<br/>以下是你的更改文件備份目錄<br/>"; //獲取檢查文件目錄 String cheakDir = propertiesBean.getValue("cheakDir"); if(!StringUtils.isEmpty(cheakDir)){ System.out.println("正在進行更改文件目錄備份。。。。。。"); //目錄分割 String[] dirs = cheakDir.split(";"); for (int i = 0; i < dirs.length; i++) { File tmp=new File(dirs[i]); List<File> list=filesBean.listFiles(dirs[i],true); //創建對應文件存儲目錄列表 File file=new File(ClassLoader.getSystemResource("").getPath()+tmp.getName()); if(!file.exists()){ try { file.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("創建目錄列表記錄文件失敗。。。。。"); return ; } } //將列表寫入文件 logFiles(file,list); //壓縮文件並加入郵件 try { ZipUtil.zipFolder(dirs[i], "tmp\\"+tmp.getName()+today+".zip"); content+=tmp.getName()+"<br/>"; email.addFiles("tmp\\"+tmp.getName()+today+".zip"); } catch (Exception e) { e.printStackTrace(); System.out.println("壓縮出錯"); return ; } } } content+="以下是你的固定備份目錄<br/>"; String zipDir = propertiesBean.getValue("zipDir"); if(!StringUtils.isEmpty(zipDir)){ System.out.println("正在進行固定備份目錄備份。。。。。。"); String[] zipDirs = zipDir.split(";"); for (int i = 0; i < zipDirs.length; i++) { try { File tmp=new File(zipDirs[i]); ZipUtil.zipFolder(zipDirs[i], "tmp\\"+tmp.getName()+today+"bak.zip"); content+=tmp.getName()+"<br/>"; email.addFiles("tmp\\"+tmp.getName()+today+"bak.zip"); } catch (Exception e) { e.printStackTrace(); System.out.println("壓縮出錯"); return ; } } } System.out.println("更新配置文件。。。。。"); propertiesBean.setValue("first", "false"); propertiesBean.setValue("lastData", today); System.out.println("發送文件。。。。。"); email.setFrom(propertiesBean.getValue("username")); email.setTOSend(propertiesBean.getValue("toSend")); email.setContent(content); email.setSubject("change2Mail備份文件"+today); if(email.send()){ System.out.println("發送成功"); }else{ System.out.println("發送失敗"); } } else { System.out.println("備份開始。。。。。"); String data=propertiesBean.getValue("lastData"); String filter=propertiesBean.getValue("filter"); String cheakDir = propertiesBean.getValue("cheakDir"); content+="你好,自"+data+"起的文件變動如下:<br/>"; if(!StringUtils.isEmpty(cheakDir)){ System.out.println("正在進行更改文件目錄備份。。。。。。"); String[] dirs = cheakDir.split(";"); for (int i = 0; i < dirs.length; i++) { File tmp=new File(dirs[i]); File file=new File(ClassLoader.getSystemResource("").getPath()+tmp.getName()); if(!file.exists()){ try { file.createNewFile(); } catch (IOException e) { System.out.println("創建文檔失敗"); e.printStackTrace(); } } content+="文件夾"+tmp.getName()+":<br/>"; List<File> list=filesBean.getModifiedFiles(dirs[i],new SimpleDateFormat("yyyyMMdd").parse(data),filter,true); List<File> now =filesBean.listFiles(dirs[i],filter,true); List<File> news=new ArrayList<File>(); List<File> del=new ArrayList<File>(); List<File> modefy=new ArrayList<File>(); List<String> lines=null; try { lines=FileUtils.readLines(file); } catch (IOException e) { e.printStackTrace(); } System.out.println("檢查新建和修改的文件。。。。。。"); for(File f:list){ if(!lines.contains(f.getAbsolutePath())){ news.add(f); }else{ modefy.add(f); } } System.out.println("檢查刪除的文件。。。。。。"); for(String f:lines){ File old=new File(f); if(!now.contains(old)){ del.add(old); } } logFiles(file,now); content+="刪除文件:<br/>"; for(File f:del){ content+="	"+f.getName()+"<br/>"; } content+="新增文件:<br/>"; for(File f:news){ content+="	"+f.getName()+"<br/>"; } content+="修改文件:<br/>"; for(File f:modefy){ content+="	"+f.getName()+"<br/>"; } try { if(list.size()!=0){ list.add(file); ZipUtil.zipFiles("tmp\\"+tmp.getName()+today+".zip", (ArrayList<File>)list); email.addFiles("tmp\\"+tmp.getName()+today+".zip"); } } catch (Exception e) { e.printStackTrace(); System.out.println("壓縮失敗"); return; } } String zipDir = propertiesBean.getValue("zipDir"); if(!StringUtils.isEmpty(zipDir)){ System.out.println("正在進行固定備份目錄備份。。。。。。"); String[] zipDirs = zipDir.split(";"); for (int i = 0; i < zipDirs.length; i++) { try { File tmp=new File(zipDirs[i]); ZipUtil.zipFolder(zipDirs[i], "tmp\\"+tmp.getName()+today+"bak.zip"); email.addFiles("tmp\\"+tmp.getName()+today+"bak.zip"); } catch (Exception e) { e.printStackTrace(); System.out.println("壓縮出錯"); return ; } } } System.out.println("更改配置文件。。。。。"); propertiesBean.setValue("lastData", today); System.out.println("發送文件。。。。。"); email.setFrom(propertiesBean.getValue("username")); email.setTOSend(propertiesBean.getValue("toSend")); email.setContent(content); email.setSubject("change2Mail備份文件"+today); if(email.send()){ System.out.println("發送成功"); }else{ System.out.println("發送失敗"); } SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); long to = df.parse(today).getTime(); long todel=Integer.parseInt(propertiesBean.getValue("time"))*(1000 * 60 * 60 * 24); Date lastBak=new Date(to-todel) ; System.out.println("刪除"+df.format(lastBak)+"之前保存的歷史文件。。。。。"); List<File> fileToDel=filesBean.getOlderFiles("tmp\\", lastBak,true); for(File f:fileToDel){ try { FileUtils.deleteQuietly(f); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); System.out.println("刪除"+f.getName()+"文件失敗"); } } System.out.println(new Date()); } } } /** * 將文件列表的絕對地址輸出到一個文件中 * @param file 輸出文件 * @param list 文件列表 * @return 成功返回true,失敗返回false */ public static boolean logFiles(File file, List<File> list) { List<String> lines=new ArrayList<String>(); try { for (File f : list) { lines.add(f.getAbsolutePath()); } FileUtils.writeLines(file, lines,false); return true; } catch (IOException e) { e.printStackTrace(); return false; } } }
配置文件: #autho:sean #data:2015/05/26 #email:hxy9104@126.com #mail config javamail的配置 mail.debug = true mail.smtp.auth = true mail.smtp.host = smtp.126.com mail.transport.protocol = smtp #mail info 郵箱設置 username = 12121@126.com password = 121212 toSend = 121212@qq.com #soft config 軟件設置 #if the fisrt time to run 是否第一次運行 first = true #the path to check 檢測的目錄 cheakDir = F:\\myfolder\\BaseClass; #the path allways to zip 完全備份的目錄 zipDir = F:\\myfolder\\myProject\\Change2Mail; #the data lastcheck 最后備份時間 lastData = 20150629 #storage life of the bak zip 保留文件 time = 5 #filter string 過濾條件 filter = .class
工具github地址:https://github.com/Seanid/change2mail
為了便於使用,工具最后導出為jar包,並編寫了一個bat進行執行,加入了jre文件夾,可以再無jdk環境下使用。最后壓縮包地址:http://pan.baidu.com/s/1i3vQJv3
本工具只是個人閑時開發,純屬娛樂,大神勿噴