/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*   モジュール名    ：JCCUnCompressFileUtil
*   ソースファイル名：JCCUnCompressFileUtil.java
*   作成者          ：富士通
*   日付            ：2011年04月25日
*＜機能概要＞
*　ZIP形式のファイルを解凍する。パスワード設定有無により
*　パスワード付圧縮ファイル、パスワードなし圧縮ファイルの解凍が可能。
*　注意：Windows環境でのパスワード付圧縮ファイルの解凍には対応していません。
*＜修正履歴＞
*   バージョン  修正日       修正者      修正内容
*   v1.00.00    2011/04/25   富士通    新規作成
*
**********************************************************************/
package eo.common.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
//import java.util.zip.ZipEntry;
//import java.util.zip.ZipInputStream;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import java.util.ArrayList;
import java.util.Enumeration;

/**
 * <dl>
 *
 * @version 1.0
 * @author 富士通株式会社
 */
public class JCCUnCompressFileUtil {

	//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start
	private static final String fileNameEncoding = "MS932";
	//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start

	
	
	/**
	 * Zip形式のファイルで解凍します。<br>
	 * 解凍処理が正常に行われた場合、解凍ファイルのファイル名込みの絶対パスを<br>
	 * String型で返却します。<br>
	 * 特記事項<br>
	 *  パスワードが設定されている場合、Linuxのunzipコマンドを用いて、パスワード付zipファイルを解凍する。
	 *  パスワードが設定されていない場合、JavaのZipInputStreamクラスを用いて、パスワードなしのzipファイルを解凍する。
	 * @param filePath 解凍前ファイルの絶対パス
	 * @param outDirPath 解凍ファイル格納先作業ディレクトリの絶対パス
	 * @param password 解凍時のパスワード
	 * @return String 解凍後ファイルの絶対パス
	 * @throws  Exception <br>
	 */
	public static String uncompressFile(String filePath,String outDirPath,String password)throws Exception{

		// パラメータチェック
		// 解凍前ファイルのFileインスタンスの生成
		File f_filePath = new  File(filePath);
		// 対象ファイルの存在チェック
		if(!f_filePath.exists() || !f_filePath.isFile() || !f_filePath.canRead()){
			throw new Exception("解凍前ファイルを読み込めません。");
		}
		
		// 解凍後ファイル格納先作業ディレクトリのFileインスタンスの生成
		File f_outDirPath = new  File(outDirPath);
		// 解凍ファイル格納先作業ディレクトリのチェック(存在チェック、ディレクトリチェック、書き込み権限チェック)
		if(!f_outDirPath.exists() || !f_outDirPath.isDirectory() || !f_outDirPath.canWrite()){			
			throw new Exception("解凍ファイル格納先作業ディレクトリに書き込めません。");
		}
		
		// パスワードチェック
		if(password == null || "".equals(password)){
			throw new Exception("パスワードに値が設定されていません。");        		
		}

		// 解凍後ファイル名
		// 解凍前ファイルから.zipを取り除く。
		String s_FilePath = outDirPath + File.separator + f_filePath.getName().substring(0,f_filePath.getName().length()-4);

		// パスワードが0の場合、パスワードなしZipファイルとして解凍する。
		if("0".equals(password)){

			//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start
			//書込用ストリーム
			//ZipInputStream zis = null;
			BufferedInputStream zis = null;
			//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start
			
			// 2013/03/11 FST_mukuo)ファイルclose用にtryの外に出す start
			ZipFile zipFile = null;
			// 2013/03/11 FST_mukuo)ファイルclose用にtryの外に出す end
			
			CheckedOutputStream cos = null;//書込用
			try {
				//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start
				// 解凍する圧縮ファイルのFileインスタンス生成
//				File zipFile = new File(filePath); 
//				zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
//				// エントリー用のファイル名の取得
//				ZipEntry entry = zis.getNextEntry();
				zipFile = new ZipFile(filePath, fileNameEncoding);
				zis = new BufferedInputStream(zipFile.getInputStream((ZipEntry)zipFile.getEntries().nextElement()), 10240);
				//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST end
				
				// 書出用のファイルを生成する。
				cos = new CheckedOutputStream(new BufferedOutputStream(new FileOutputStream(s_FilePath)), new CRC32());

				int len;
				// バイナリ文書読み込み
				while ((len = zis.read()) != -1) { 
					cos.write(len);
				}
				cos.flush();
				
			}catch(Exception e){
				throw new Exception("zipファイル解凍処理にて例外が発生しました。", e);                
			}finally{
				try{
					// 2013/03/11 FST_mukuo)ファイルclose start
					if(zipFile != null)
					{
						zipFile.close();
					}
					// 2013/03/11 FST_mukuo)ファイルclose end
					// CheckedInputStreamのクローズができていない場合
					if(cos != null){
						// CheckedInputStreamをクローズをする。
						cos.close();
					}
					// ZipOutputStreamのクローズができていない場合
					if(zis != null){
						//ZipOutputStreamをクローズする。
						zis.close();
					}
				}catch(Exception ex){
				}        	
			}
			// パスワードが0以外の場合、パスワード付きZipファイルとして解凍する。
		}else{

			String osname = System.getProperty("os.name");
			// Windowsであった場合、作成したディレクトリを削除して、例外をスロー。
			if(osname.indexOf("Windows")>=0){
				throw new Exception("Windows環境では、パスワード付きZipファイルの解凍はできません。");
			}
			// 外部プロセス実行コマンド
			try{
				// コマンド実行が終了するまで待ち、終了ステータスを判定し、0以外であった場合、例外をスローする。
				Process proc = Runtime.getRuntime().exec(new String[]{"/usr/bin/unzip", "-P", password, filePath, "-d", outDirPath});
				if(proc.waitFor()!=0){
					throw new Exception("zipファイル解凍処理にて例外が発生しました。");
				}
				proc.destroy();

			}catch(Exception e){
				// exceptionを取得し、throwする。
				throw new Exception("zipファイル解凍処理にて例外が発生しました。", e);                
			}

		}

		// 圧縮ファイルパスの返却
		return s_FilePath;

	}
	
//	20110630 FST zipファイル内に複数ファイルが格納されている場合の処理用メソッドを追加 strat
	
	/**
	 * Zip形式のファイルで解凍します。<br>
	 * 解凍処理が正常に行われた場合、解凍ファイルのファイル名込みの絶対パスをString[]型の配列で返却します。
	 * 特記事項<br>
	 *  パスワードが設定されている場合、Linuxのunzipコマンドを用いて、パスワード付zipファイルを解凍します。
	 *  パスワードが設定されていない場合、JavaのZipInputStreamクラスを用いて、パスワードなしのzipファイルを解凍します。
	 *  指定の解凍先ディレクトリに任意のファイルが既に存在している場合は、例外を通知します。
	 *  解凍後のファイルにディレクトリを含む場合はディレクトリは無視します。
	 * @param filePath 解凍前zipファイルの絶対パス
	 * @param outDirPath 解凍ファイル格納先作業ディレクトリの絶対パス
	 * @param password 解凍時のパスワード
	 * @return String[] 解凍後ファイルの絶対パスの配列
	 * @throws  Exception <br>
	 */
	public static String[] uncompressFileMulti(String filePath, String outDirPath, String password)throws Exception
	{
		// パラメータチェック
		// 解凍前ファイルのFileインスタンスの生成
		File f_filePath = new  File(filePath);
		
		// 対象ファイルの存在チェック
		if(!f_filePath.exists() || !f_filePath.isFile() || !f_filePath.canRead())
		{
			throw new Exception("解凍前ファイルを読み込めません。");
		}
		// 解凍後ファイル格納先作業ディレクトリのFileインスタンスの生成
		File f_outDirPath = new File(outDirPath);
		
		// 解凍ファイル格納先作業ディレクトリのチェック(存在チェック、ディレクトリチェック、書き込み権限チェック)
		if(!f_outDirPath.exists() || !f_outDirPath.isDirectory() || !f_outDirPath.canWrite())
		{
			throw new Exception("解凍ファイル格納先作業ディレクトリに書き込めません。");
		}
		// パスワードチェック
		if(password == null || "".equals(password))
		{
			throw new Exception("パスワードに値が設定されていません。");
		}
		// 解凍後ファイルフォルダ（引数で指定されたフォルダ＋セパレータ）
		String s_FilePath = outDirPath + File.separator;
		// 解凍後ファイルパスの格納用領域
		ArrayList<String> pathArray = new ArrayList<String>();
		
		// パスワードが0の場合、パスワードなしZipファイルとして解凍する。
		if("0".equals(password))
		{
			//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start
			//書込用ストリーム
			//ZipInputStream zis = null;
			BufferedInputStream zis = null;
			//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start
			
			// 2013/03/11 FST_mukuo)ファイルclose用にtryの外に出す start
			ZipFile zipFile = null;
			// 2013/03/11 FST_mukuo)ファイルclose用にtryの外に出す end
			
			CheckedOutputStream cos = null;//書込用
			try
			{
//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST start			
				// 解凍する圧縮ファイルのFileインスタンス生成
//				File zipFile = new File(filePath); 
//				zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile), 10240));
				zipFile = new ZipFile(filePath, fileNameEncoding);
				
				// エントリー用のファイルインスタンスの取得
//				ZipEntry entry = zis.getNextEntry();
				Enumeration enm = zipFile.getEntries();
				ZipEntry entry = null;
				
				// zipで圧縮されたファイルの数だけ解答処理を行う
				//while(null != entry)
				while(enm.hasMoreElements())
				{
					entry = (ZipEntry)enm.nextElement();
					if(entry.isDirectory())
					{
						//entry = zis.getNextEntry();
						continue;
					}
					
					zis = new BufferedInputStream(zipFile.getInputStream(entry), 10240);
					
					// 書出用のファイルを生成する。
					cos = new CheckedOutputStream(new BufferedOutputStream(new FileOutputStream(s_FilePath + entry.getName()), 10240), new CRC32());
					
					// 返却用のリストにファイルパスをセット
					pathArray.add(s_FilePath + entry.getName());
					
					int len = -1;
					// バイナリ文書読み込み
					while ((len = zis.read()) != -1)
					{ 
						cos.write(len);
					}
					// ファイルの書き込み＆ストリームをクローズ
					cos.flush();
					cos.close();
					
					//次のファイルインスタンスを取得
					//entry = zis.getNextEntry();
//2012/10/24 zipファイル内に日本語ファイル名が存在する場合の対応 FST end
				}
				
			}
			catch(Exception e)
			{
				throw new Exception("zipファイル解凍処理にて例外が発生しました。", e);
			}
			finally
			{
				try
				{
					// 2013/03/11 FST_mukuo)ファイルclose start
					if(zipFile != null)
					{
						zipFile.close();
					}
					// 2013/03/11 FST_mukuo)ファイルclose end
					// CheckedInputStreamのクローズができていない場合
					if(cos != null)
					{
						// CheckedInputStreamをクローズをする。
						cos.close();
					}
					// ZipOutputStreamのクローズができていない場合
					if(zis != null)
					{
						//ZipOutputStreamをクローズする。
						zis.close();
					}
				}
				catch(Exception ex)
				{
					// ストリームクローズ失敗は何もしない
					;
				}
			}
		// パスワードが0以外の場合、パスワード付きZipファイルとして解凍する。
		}
		else
		{
			String osname = System.getProperty("os.name");
			// Windowsであった場合、作成したディレクトリを削除して、例外をスロー。
			if(osname.indexOf("Windows") >= 0)
			{
				throw new Exception("Windows環境では、パスワード付きZipファイルの解凍はできません。");
			}
			// 外部プロセス実行コマンド
			try
			{
				// コマンド実行が終了するまで待ち、終了ステータスを判定し、0以外であった場合、例外をスローする。
				Process proc = Runtime.getRuntime().exec(new String[]{"/usr/bin/unzip", "-P", password, filePath, "-d", outDirPath});
				if(proc.waitFor()!=0)
				{
					throw new Exception("zipファイル解凍処理にて例外が発生しました。");
				}
				proc.destroy();
				
				// 解凍後のファイルパスリストを生成する
				File[] fileList = f_outDirPath.listFiles();
				for(int i = 0; i < fileList.length; i++)
				{
					pathArray.add(fileList[i].getAbsolutePath());
				}
			}
			catch(Exception e)
			{
				// exceptionを取得し、throwする。
				throw new Exception("zipファイル解凍処理にて例外が発生しました。", e);
			}
		}
		// 解答後ファイルパスのリストを返却
		return (String[])pathArray.toArray(new String[0]);
	}
	
//	20110630 FST zipファイル内に複数ファイルが格納されている場合の処理用メソッドを追加 end
	
}
