001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.util; 017 018import java.io.BufferedInputStream; 019import java.io.BufferedOutputStream; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileNotFoundException; 023import java.io.FileOutputStream; 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027import java.util.zip.ZipEntry; 028import java.util.zip.ZipInputStream; 029import java.util.zip.ZipOutputStream; 030 031/** 032 * ZipFileUtil.java は、ZIPファイルの解凍・圧縮を行うためのUtilクラスです。 033 * 034 * @og.group ユーティリティ 035 * 036 * @version 4.1 037 * @author Hiroki Nakamura 038 * @since JDK5.0, 039 */ 040public final class ZipFileUtil { 041 042 /** ファイル読み込み時のバッファサイズ */ 043 private static final int BUF_SIZE = 1024; 044 045 /** 046 * 全てスタティックメソッドのためインスタンスの作成を禁止します。 047 */ 048 private ZipFileUtil() {}; 049 050 /** 051 * 引数に指定されたZIPファイルをフォルダに解凍します。 052 * 解凍先のファイルが存在する場合でも、上書きされますので注意下さい。 053 * 054 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 055 * @og.rev 4.3.1.1 (2008/08/23) mkdirs の戻り値判定 056 * @og.rev 4.3.3.3 (2008/10/22) mkdirsする前に存在チェック 057 * @og.rev 5.1.9.0 (2010/08/01) 更新時刻の設定 058 * 059 * @param targetPath 解凍先のフォルダ 060 * @param zipFileName 解凍するZIPファイル 061 * 062 * @return 解凍されたZIPファイルの一覧 063 */ 064 public static List<String> unCompress( final String targetPath, final String zipFileName ) { 065 // 解凍先フォルダの末尾が'/'又は'\'でなければ区切り文字を挿入 066 String tmpPrefix = targetPath; 067 if( File.separatorChar != targetPath.charAt( targetPath.length() - 1 ) ) { 068 tmpPrefix = tmpPrefix + File.separator; 069 } 070 071 List<String> list = new ArrayList<String>(); 072 ZipInputStream zis = null; 073 ZipEntry entry = null; 074 BufferedOutputStream out = null; 075 String fileName = null; 076 File tmpFile = null; 077 try { 078 zis = new ZipInputStream( new BufferedInputStream( new FileInputStream( zipFileName ) ) ); 079 080 while( ( entry = zis.getNextEntry() ) != null ) { 081 fileName = tmpPrefix + entry.getName().replace( '/', File.separatorChar ); 082 list.add( fileName ); 083 084 boolean flag = true; // 4.3.1.1 (2008/08/23) mkdirs の戻り値判定 085 tmpFile = new File( fileName ); 086 // ディレクトリの場合は、自身を含むディレクトリを作成 087 if( entry.isDirectory() ) { 088 // 4.3.3.3 (2008/10/22) 作成する前に存在チェック 089 if( !tmpFile.exists() ) { 090 flag = tmpFile.mkdirs(); 091 } 092 } 093 // ディレクトリの場合は、自身の親となるディレクトリを作成 094 else { 095 // 4.3.3.3 (2008/10/22) 作成する前に存在チェック 096 if( !tmpFile.getParentFile().exists() ) { 097 flag = new File( fileName ).getParentFile().mkdirs(); 098 } 099 100 out = new BufferedOutputStream( new FileOutputStream( fileName ) ); 101 byte[] buf = new byte[BUF_SIZE]; 102 int count = 0; 103 while( ( count = zis.read( buf ) ) != -1 ) { 104 out.write( buf, 0, count ); 105 } 106 out.close(); 107 } 108 // 4.3.1.1 (2008/08/23) mkdirs の戻り値判定 109 if( ! flag ) { System.err.println( fileName + " の ディレクトリ作成に失敗しました。" ); } 110 // 5.1.9.0 (2010/08/01) 更新時刻の設定 111 long lastTime = entry.getTime(); 112 if( lastTime >= 0 ) { 113 flag = tmpFile.setLastModified( lastTime ); 114 } 115 if( ! flag ) { System.err.println( fileName + " の 更新時刻の設定に失敗しました。" ); } 116 } 117 } 118 catch( FileNotFoundException ex ) { 119 String errMsg = "解凍ファイルが作成できません。[ファイル名=" + fileName + "]"; 120 throw new RuntimeException( errMsg, ex ); 121 } 122 catch( IOException ex ) { 123 String errMsg = "ZIPファイルの解凍に失敗しました。[ファイル名=" + fileName + "]"; 124 throw new RuntimeException( errMsg, ex ); 125 } 126 finally { 127 Closer.ioClose( zis ); 128 Closer.ioClose( out ); 129 } 130 131 return list; 132 } 133 134 /** 135 * 引数に指定されたファイル又はフィルダ内に存在するファイルをZIPファイルに圧縮します。 136 * 圧縮レベルはデフォルトのDEFAULT_COMPRESSIONです。 137 * 圧縮ファイルのエントリー情報として本来は、圧縮前後のファイルサイズ、変更日時、CRCを登録する 138 * 必要がありますが、ここでは高速化のため、設定していません。(特に圧縮後ファイルサイズの取得は、 139 * 非常に不可がかかる。) 140 * このため、一部のアーカイバでは正しく解凍できない可能性があります。 141 * 既にZIPファイルが存在する場合でも、上書きされますので注意下さい。 142 * 143 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 144 * 145 * @param targetPath 圧縮対象のファイル又はフォルダ 146 * @param zipFileName ZIPファイル名 147 * 148 * @return ZIPファイルのエントリーファイル名一覧 149 */ 150 public static List<String> compress( final String targetPath, final String zipFileName ) { 151 List<String> list = new ArrayList<String>(); 152 ZipOutputStream zos = null; 153 154 try { 155 zos = new ZipOutputStream( new BufferedOutputStream ( new FileOutputStream( zipFileName ) ) ); 156 File target = new File( targetPath ); 157 158 // ZIP圧縮処理を行います 159 addZipEntry( zos, list, target, "", 0 ); 160 161 zos.close(); 162 } 163 catch( FileNotFoundException ex ) { 164 String errMsg = "ZIPファイルが見つかりません。[ファイル名=" + zipFileName + "]"; 165 throw new RuntimeException( errMsg, ex ); 166 } 167 catch( IOException ex ) { 168 String errMsg = "ZIP圧縮に失敗しました。[ファイル名=" + zipFileName + "]"; 169 throw new RuntimeException( errMsg, ex ); 170 } 171 finally { 172 Closer.ioClose( zos ); 173 } 174 175 return list; 176 } 177 178 /** 179 * ZIP圧縮処理を行います。 180 * 引数に指定されたFileオブジェクトがディレクトリであれば再帰的に呼び出し、 181 * 下層のファイルをエントリーします。但し、そのディレクトリ自身が空である場合は、 182 * ディレクトリをエントリー情報として設定します。 183 * 184 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 185 * @og.rev 5.1.9.0 (2010/08/01) 更新時刻の設定 、BufferedInputStream のスコープを小さくする。 186 * 187 * @param zos ZIP用OutputStream 188 * @param list ZIPファイルのエントリーファイル名一覧 189 * @param target 圧縮対象のファイルオブジェクト 190 * @param prefix 処理中のディレクトリ 191 * @param depth 階層 192 */ 193 private static void addZipEntry( final ZipOutputStream zos, final List<String> list, final File target, final String prefix, final int depth ) { 194// BufferedInputStream in = null; 195 196 try { 197 // ターゲットがディレクトリの場合は、ファイルが含まれているかを 198 // チェックし、空ならばそのディレクトリをエントリーに追加する。(最後に'/'が必要) 199 // 空じゃなければ、再起呼び出し 200 if( target.isDirectory() ) { 201 File[] fileList = target.listFiles(); 202 if( fileList.length == 0 ) { 203 list.add( prefix + target.getName() ); 204 ZipEntry entry = new ZipEntry( prefix + target.getName() + '/' ); 205 zos.putNextEntry( entry ); 206 zos.closeEntry(); // ディレクトリのエントリーは空で作成する必要がある。但し、FindBugsはエラー 207 } 208 else { 209 for( int i = 0; i < fileList.length; i++ ) { 210 // 再起呼び出しを行う際、圧縮対象にフォルダが指定された場合、 211 // 最初の再起処理時は、エントリーにフォルダのパスを含めないようにする。 212 String nextPrefix = ""; 213 if( depth > 0 ) { 214 nextPrefix = prefix + target.getName() + '/'; 215 } 216 addZipEntry( zos, list, fileList[i], nextPrefix, depth + 1 ); 217 } 218 } 219 } 220 // ターゲットがファイルの場合 221 else { 222 list.add( prefix + target.getName() ); 223 ZipEntry entry = new ZipEntry( prefix + target.getName() ); 224 entry.setTime( target.lastModified() ); // 5.1.9.0 (2010/08/01) 更新時刻の設定 225 zos.putNextEntry( entry ); 226 BufferedInputStream in = null; 227 228 try { 229 in = new BufferedInputStream( new FileInputStream( target.getAbsolutePath() ) ); 230 byte[] buf = new byte[BUF_SIZE]; 231 int count; 232 while( ( count = in.read( buf, 0, BUF_SIZE ) ) != -1 ) { 233 zos.write( buf, 0, count ); 234 } 235 } 236 finally { 237// in.close(); 238 Closer.ioClose( in ); 239 } 240 zos.closeEntry(); 241 } 242 } 243 catch( FileNotFoundException ex ) { 244 String errMsg = "圧縮対象のファイルが見つかりません。[ファイル名=" + target.getName() + "]"; 245 throw new RuntimeException( errMsg, ex ); 246 } 247 catch( IOException ex ) { 248 String errMsg = "ZIP圧縮に失敗しました。[ファイル名=" + target.getName() + "]"; 249 throw new RuntimeException( errMsg, ex ); 250 } 251// finally { 252// Closer.ioClose( in ); 253// } 254 } 255 256 /** 257 * ファイルの圧縮または解凍を行います。 258 * 259 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 260 * @og.rev 5.9.21.1 (2017/06/12) 結果出力追加 261 * 262 * 使用方法 : java [comp or uncomp] [targetPath] [zipFileName] 263 * 第1引数 : comp:圧縮 uncomp:解凍 264 * 第2引数 : 圧縮時:圧縮対象のファイル又はフォルダ 解凍時:解凍先のフォルダ 265 * 第3引数 : ZIPファイル名 266 * 267 * @param args パラメータ 268 */ 269 public static void main( final String[] args ) { 270 String usage = "Usage: java [comp or uncomp] [targetPath] [zipFileName]"; 271 if( args.length < 3 ) { 272 System.out.println( usage ); 273 return; 274 } 275 276 // 開始時間 277 long start = System.currentTimeMillis(); 278 279 List<String> list = null; 280 if( "comp".equals( args[0] ) ) { 281 list = compress( args[1], args[2] ); 282 } 283 else if( "uncomp".equals( args[0] ) ) { 284 list = unCompress( args[1], args[2] ); 285 } 286 else { 287 System.out.println( usage ); 288 return; 289 } 290 291 if( list != null ) { 292 // 結果を表示 293 for( String fileName : list ) { 294 System.out.println( fileName ); 295 } 296 // 処理時間を表示 297// System.out.println( "処理時間 : " + String.valueOf( System.currentTimeMillis() - start ) + "(ms)" ); 298 System.out.println( "処理時間 : " + ( System.currentTimeMillis() - start ) + "(ms)" ); 299 } 300 else{ 301 System.out.println( "list is null." ); // 5.9.21.1 (2017/06/16) 302 } 303 } 304}