Java - Use Java Runtime to execute 7za.exe compress and extract commands

之前曾經有使用過Java來執行外部執行檔(如:.exe),進而產生出所需要的結果檔案。

而在這邊主要是使用7za.exe執行檔來進行壓縮及解壓縮的動作,紀錄一下如果使用7za.exe

需要搭配什麼樣的指令才可以達到基本壓縮及解壓縮的動作,而也會討論到使用Process時

需要注意的一些事情。

基本上在使用Java來執行外部執行檔時,需要另外定義一process,相對主程式而言為

subprocess。process需要透過Runtime.getRuntime().exec(command)來產生實體,當下也已經

做執行的動作了。

而process本身有下列幾項的函式可以使用,如exitValue,可以得到執行結果,印出0表示正

常的終止;waitFor,表示主程式需等待subprocess執行完成,並回傳如exitValue的執行結果,

才會終止結束程式,但是如果subprocess執行的當下發生錯誤,會hang住主程式,導致無法

繼續正常運作。


在此,範例程式如下:
public static void main(String[] args) {
  // TODO Auto-generated method stub
   String root = "C:\\Java program\\Java Zip";
   String tmpFolderPath = root+"\\dirs\\*.txt";
   String zipPath = "tools\\7za920";
   SimpleDateFormat date_convert = new SimpleDateFormat("yyyyMMddHHmmss");
   String report_dname = date_convert.format(new java.sql.Date(System.currentTimeMillis()));
   String outFolderPath = "output\\"+report_dname+".7z";
   
   String zip_exe[]={zipPath + "\\7za.exe","a","-t7z",outFolderPath,tmpFolderPath};
   System.out.println("壓縮執行結果-Process exitValue: " + execCommand(zip_exe));
   zip_exe=new String[]{zipPath + "\\7za.exe","x",outFolderPath, "-o"+root+"\\output\\"+report_dname};
   System.out.println("解壓縮執行結果-Process exitValue: " + execCommand(zip_exe));
}

public static int execCommand(String zip_exe[]){
   Process p = null;
   InputStream in_b = null;
   BufferedReader reader = null;
   int exitVal = 0;
   try{
      p = Runtime.getRuntime().exec(zip_exe);
      //in_b = p.getInputStream();
      //reader = new BufferedReader(new InputStreamReader(in_b));   
      String str = null;
          System.out.println("<output>");
//          while ((str = reader.readLine()) != null){
//           System.out.println(str);
//          }
          System.out.println("</output>");
          exitVal = p.exitValue();            
   }/*catch(InterruptedException e){
          e.printStackTrace();
   }*/catch(IOException e){
          e.printStackTrace();
   }finally{
          p.destroy();
//        try {
//           in_b.close();
//           reader.close();
//   } catch (IOException e) {
//        // TODO Auto-generated catch block
//        e.printStackTrace();
//   }
         }
         return exitVal;
 }

在這邊宣告zip_exe array,組成執行壓縮及解壓縮的指令

執行壓縮

參數1: 7za.exe的執行檔絕對路徑

參數2: a => 壓縮指令

參數3: -t7z 壓縮方式

參數4: 輸出壓縮檔的路徑

參數5: 檔案的來源路徑

ps. 假設參數5所帶入為相對路徑,檔案在壓縮的當下會將存放檔案的目錄一起壓縮進去,

填入絕對路徑的話,則壓縮檔內只有檔案本身!

解壓縮

參數1: 7za.exe的執行檔絕對路徑

參數2: x => 解壓縮指令

參數3: 壓縮檔案的來源路徑

參數4: -oC:\\目錄名稱

ps. 參數4 -o後的路徑為解壓縮後檔案存放的地方,若目錄不存在的話會自動建立!

當下的執行結果如下:


撇開註解掉的程式碼,當下如果執行的話,會出現錯誤!

主要訊息為 java.lang.IllegalThreadStateException: process has not exited

表示subprocess在還沒有執行完的當下,主程式就已經結束了,導致subprocess被不正常的

強制結束!!

此時將31行的程式改成 => exitVal = p.waitFor();

並且32 ~ 34行的InterruptedException catch註解也拿掉!

此時執行結果如下:


由上面曾經提到的waitFor功用,使得subprocess所執行的壓縮及解壓縮可以順利完成!


而我們還是決定改回exitValue,而程式又要順利執行,程式碼可以修改如下:

try{
    p = Runtime.getRuntime().exec(zip_exe);
    in_b = p.getInputStream();
    reader = new BufferedReader(new InputStreamReader(in_b));   
    String str = null;
    System.out.println("<output>");
    while ((str = reader.readLine()) != null){
        System.out.println(str);
    }
    System.out.println("</output>");
    exitVal = p.exitValue();            
   }/*catch(InterruptedException e){
       e.printStackTrace();
   }*/catch(IOException e){
       e.printStackTrace();
   }finally{
       p.destroy();
       try {
          in_b.close();
          reader.close();
       } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
       }
   }

在此將讀取Process在執行7za.exe過程之中執行指令的InputStream資料,並且將此過程列印

出來,這個目的主要也代表了主程式當下也在等待subprocess的執行過程,如此一來可能不

會自行先結束!

留言