/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*   モジュール名    ：JCCCompressFileUtil
*   ソースファイル名：JCCCompressFileUtil.java
*   作成者          ：富士通
*   日付            ：2011年04月25日
*＜機能概要＞
*　ZIP形式でファイル圧縮をおこなう。パスワード設定フラグの切り替えにより
*　パスワード付圧縮ファイル、パスワードなし圧縮ファイルを作成。
*　注意：Windows環境でのパスワード付圧縮ファイル作成には対応していません。
*＜修正履歴＞
*   バージョン  修正日       修正者      修正内容
*   v1.00.00    2011/04/25   富士通    新規作成
*
**********************************************************************/
package eo.common.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
//import java.util.zip.ZipEntry;
//import java.util.zip.ZipOutputStream;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;

/**
 * <dl>
 *
 * @version 1.0
 * @author 富士通株式会社
 */
public class JCCCompressFileUtil {

	//2012/10/24 zipファイルに内包するファイル名の文字コード指定 FST start
	private static final String fileNameEncoding = "MS932";
	//2012/10/24 zipファイルに内包するファイル名の文字コード指定 FST end

	/**
	 * ファイルをZip形式で圧縮します。<br>
	 * 圧縮処理が正常に行われた場合、圧縮ファイルのファイル名込みの絶対パスを<br>
	 * String型で返却します。<br>
	 * 特記事項<br>
	 *  パスワード設定フラグが"1"の場合、Linuxのzipコマンドを用いて、パスワード付zipファイルを作成します。
	 *  パスワード設定フラグが"0"の場合、JavaのZipOutputStreamクラスを用いて、パスワードなしのzipファイルを作成します。
	 *  よってWindowsで動作する場合、引数のパスワード設定フラグは常に"0"を指定してください。
	 *  パスワード付Zipファイルとして圧縮する場合、第三引数で指定したパスワードを使用します。<br>
	 *  圧縮後ファイルは、圧縮ファイル格納先作業ディレクトリの絶対パス/UID（マシンで一意のID。自動生成）/圧縮前ファイル名（拡張子付）.zip<br>
	 *  で作成されます。<br>
	 *  ファイルの親ディレクトリ名に使用するIDは、本メソッドで自動採番するため、ファイル作成後、圧縮ファイル<br>
	 *  の絶対パスを返却します。
	 * @param filePath 圧縮前ファイルの絶対パス
	 * @param outDirPath 圧縮ファイル格納先作業ディレクトリの絶対パス
	 * @param password 圧縮ファイルに付与するパスワード
	 * @param passFlg パスワード設定フラグ
	 * @param compLevel 圧縮レベル<br>
	 * @return String 生成した圧縮ファイルの絶対パス
	 * @throws  Exception <br>
	 * 以下の場合、明示的にExceptionをスローします。<br>
	 * １）圧縮前ファイルが存在しない、もしくはファイルではない場合<br>
	 * ２）圧縮ファイル格納先作業ディレクトリが存在しない、もしくはディレクトリではない場合
	 * ３）圧縮前ファイル名の桁数が100桁を超えている場合
	 * ４）パスワード設定フラグの値がnull、もしくは"0"でも"1"でもない場合
	 * ５）圧縮レベルの値がnull、もしくは"1"〜"9"の範囲にない場合
	 * ６）圧縮ファイルの絶対パスが重複した場合
	 * ７）zipファイル圧縮処理にて例外が発生した場合
	 */
	public static String compressFile(String filePath,String outDirPath,String password,String passFlg,String compLevel)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("圧縮ファイル格納先作業ディレクトリに書き込めません。");
		}
		// 圧縮前ファイルの名前（拡張子付）
		String fileName = f_filePath.getName();
		
		// 2012/10/24 ファイル名の桁数制限を100桁→127桁に変更 FST start
		// 圧縮前ファイル名の長さチェック（100桁より多いファイル名はエラーとする）
//		if(fileName.length() > 100){
//			throw new Exception("ファイル名が100桁を超えています。"); 
//		}
		if(fileName.length() > 127){
			throw new Exception("ファイル名が127桁を超えています。"); 
		}
		// 2012/10/24 ファイル名の桁数制限を100桁→127桁に変更 FST end
		

		// パスワード設定フラグのチェック
		if(passFlg == null || (!"0".equals(passFlg) && !"1".equals(passFlg))){
			throw new Exception("パスワード設定フラグに無効な値が渡されました。:"+passFlg);        		
		}

		// 圧縮レベルのチェック
		int i_compLevel = 99;
		try {
			i_compLevel = Integer.parseInt(compLevel);
		} catch (Exception e) {
			throw new Exception("圧縮レベルに無効な値が渡されました。",e);                    		
		}

		if((1 <= i_compLevel && i_compLevel <= 9)==false){
			throw new Exception("圧縮レベルは1から9までの範囲で設定してください。:"+i_compLevel);

		}

		// 圧縮後ファイルのフルパス
		String s_ZipFilePath = null;

		// 2009.07.22 FST ファイル操作共通化 STR
		// ディレクトリが重複しないようにマシンで一意であるUIDを取得し、作業ディレクトリ名とする。
		s_ZipFilePath = JCCFileUtil.createDirUid(outDirPath) + File.separator + fileName + ".zip";
//		String uid = new UID().toString();
//		// ファイル名に使用しない":"や"-"は、"_"に変換する。
//		uid = uid.replace(":", "_");
//		uid = uid.replace("-", "0");
//
//
//		// 圧縮ファイル格納先
//		//{圧縮ファイル保存先}/uid/ファイル名 + .zip
//		s_ZipFilePath = outDirPath + File.separator +"_"+ uid +  File.separator + fileName +  ".zip";

		// ディレクトリを作成
		// すでにディレクトリが存在している場合は例外をスロー
		File f_ZipFilePath = JCCFileUtil.createDir(s_ZipFilePath);
//		File f_ZipFilePath = new File(s_ZipFilePath);
//		if(f_ZipFilePath.getParentFile().mkdir()==false){
//			throw new Exception("圧縮ファイルのパスが重複しました。：" + s_ZipFilePath);
//		}
		// 2009.07.22 FST ファイル操作共通化 END

		// パスワード設定フラグが0の場合、パスワード設定なしの圧縮ファイルを作成する。
		if("0".equals(passFlg)){

			//読込用ストリーム
			CheckedInputStream cis = null;
			//書込用ストリーム
			ZipOutputStream zos = null;
			try {

				// 出力する圧縮ファイルのFileインスタンス生成
				File zipFile = new File(s_ZipFilePath); 
				
				zos = new ZipOutputStream(new FileOutputStream(zipFile));
				//2012/10/24 zipファイルに内包するファイル名の文字コード指定 FST start
				zos.setEncoding(fileNameEncoding);
				//2012/10/24 zipファイルに内包するファイル名の文字コード指定 FST end
				
				// エントリー用のファイル名の取得
				String strZipName = zipFile.getName().substring(0,zipFile.getName().lastIndexOf("."));
				ZipEntry ztarget= new ZipEntry(strZipName); 
				// 圧縮レベルを設定をする
				zos.setLevel(i_compLevel);
				// ZIP ファイルエントリの書き込みを開始
				zos.putNextEntry(ztarget);
				// 読込データのチェックサムも保持する入力ストリームを生成
				cis = new CheckedInputStream(new BufferedInputStream(new FileInputStream(f_filePath)), new CRC32());

				//ファイルサイズ
				int filesize = 0;
				//読込サイズ
				int readsize = 0;
				//データの読込先のバッファ
				byte buf[] = new byte[5120];
				//読込ファイルが終了するまで読み込み、出力ストリームで書き出す。
				while((readsize = cis.read(buf, 0, buf.length)) != -1){
					filesize += readsize;
					zos.write(buf, 0, readsize);
				}
				//WindowsXPに付属している解凍ソフトでは圧縮解除されたエントリデータの CRC-32 チェックサム
				//と、圧縮解除時のファイルサイズを設定しておかないと解凍できない。
				//CRC32チェックサム
				ztarget.setCrc(cis.getChecksum().getValue());
				//圧縮解除時サイズ
				ztarget.setSize(filesize);

			}catch(Exception e){
				throw new Exception("zipファイル圧縮処理にて例外が発生しました。", e);                
			}finally{
				try{
					// CheckedInputStreamのクローズができていない場合
					if(cis != null){
						// CheckedInputStreamをクローズをする。
						cis.close();
					}
					// ZipOutputStreamのクローズができていない場合
					if(zos != null){
						//ZipOutputStreamをクローズする。
						zos.close();
					}
				}catch(Exception ex){
				}        	
			}
			// パスワード設定フラグが1の場合、パスワード設定付きの圧縮ファイルを作成する。
		}else if("1".equals(passFlg)){

			String osname = System.getProperty("os.name");
			// Windowsであった場合、作成したディレクトリを削除して、例外をスロー。
			if(osname.indexOf("Windows")>=0){
				// 作成したディレクトリを削除
				f_ZipFilePath.getParentFile().delete();
				throw new Exception("Windows環境では、パスワード設定ありの設定はできません。");
			}
			// 外部プロセス実行コマンド
			try{
				// コマンド実行が終了するまで待ち、終了ステータスを判定し、0以外であった場合、例外をスローする。
				Process proc = Runtime.getRuntime().exec(new String[]{"/usr/bin/zip", "-" + compLevel, "-P", password, "-j", s_ZipFilePath, filePath});
				if(proc.waitFor()!=0){
					throw new Exception("zipファイル圧縮処理にて例外が発生しました。");
				}
				proc.destroy();

			}catch(Exception e){
				// exceptionを取得し、throwする。
				throw new Exception("zipファイル圧縮処理にて例外が発生しました。", e);                
			}

		}

		// 圧縮ファイルパスの返却
		return s_ZipFilePath;

	}
}
