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.File; 019import java.io.InputStream; 020import java.io.FileInputStream; 021import java.io.BufferedInputStream; 022import java.io.FileNotFoundException; 023import java.io.FileOutputStream; 024import java.io.BufferedOutputStream; 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.List; 028// import java.util.zip.ZipEntry; 029// import java.util.zip.ZipInputStream; 030// import java.util.zip.ZipOutputStream; 031 032import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; 033import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; 034import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; 035import org.apache.commons.compress.utils.IOUtils; 036 037/** 038 * ZipArchive.java は、ZIPファイルの解凍・圧縮を行うためのUtilクラスです。 039 * 040 * zipファイルで、圧縮時のファイルのエンコードを指定できるようにします。 041 * ファイルをZIPにするには、java.util.zipパッケージ を利用するのが一般的です。 042 * ところが、ファイル名にUTF-8文字エンコーディングを利用する為、Windowsの世界では 043 * これを取り扱うアプリケーションも少ないため、文字化けして見えてしまいます。 044 * これを解決するには、エンコードが指定できるアーカイバをる要する必要があります。 045 * 有名どころでは、ant.jar に含まれる、org.apache.tools.zip と、Apache Commons の 046 * org.apache.commons.compress です。 047 * org.apache.tools.zip は、java.util.zip とほぼ同じ扱い方、クラス名が使えるので 048 * 既存のアプリケーションを作り変えるには、最適です。 049 * openGion では、アーカイバ専用ということで、org.apache.commons.compress を 050 * 採用します。 051 * 052 * @og.group ユーティリティ 053 * @og.rev 6.0.0.0 (2014/04/11) org.apache.commons.compress パッケージの利用(日本語ファイル名対応) 054 * 055 * @version 6.0 056 * @author Kazuhiko Hasegawa 057 * @since JDK5.0, 058 */ 059public final class ZipArchive { 060 061 /** 062 * 全てスタティックメソッドのためインスタンスの作成を禁止します。 063 */ 064 private ZipArchive() {}; 065 066 /** 067 * エンコードに、Windows-31J を指定した、ZIPファイルの解凍処理を行います。 068 * 引数にフォルダ(targetPath)に指定されたZIPファイル(zipFile)を解凍します。 069 * 解凍先のファイルが存在する場合でも、上書きされますので注意下さい。 070 * 071 * @og.rev 5.7.1.2 (2013/12/20) org.apache.commons.compress パッケージの利用(日本語ファイル名対応) 072 * 073 * @param targetPath 解凍先のフォルダ 074 * @param zipFile 解凍するZIPファイル 075 * 076 * @return 解凍されたZIPファイルの一覧 077 */ 078 public static List<File> unCompress( final File targetPath , final File zipFile ) { 079 return unCompress( targetPath,zipFile,"Windows-31J" ); 080 } 081 082 /** 083 * エンコードを指定した、ZIPファイルの解凍処理を行います。 084 * 引数にフォルダ(targetPath)に指定されたZIPファイル(zipFile)を解凍します。 085 * 解凍先のファイルが存在する場合でも、上書きされますので注意下さい。 086 * 087 * 解凍途中のエラーは、エラー出力に出力するだけで、処理は止めません。 088 * 089 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 090 * @og.rev 4.3.1.1 (2008/08/23) mkdirs の戻り値判定 091 * @og.rev 4.3.3.3 (2008/10/22) mkdirsする前に存在チェック 092 * @og.rev 5.1.9.0 (2010/08/01) 更新時刻の設定 093 * @og.rev 5.7.1.2 (2013/12/20) org.apache.commons.compress パッケージの利用(日本語ファイル名対応) 094 * 095 * @param targetPath 解凍先のフォルダ 096 * @param zipFile 解凍するZIPファイル 097 * @param encording ファイルのエンコード(Windows環境では、"Windows-31J" を指定します) 098 * 099 * @return 解凍されたZIPファイルの一覧 100 */ 101 public static List<File> unCompress( final File targetPath, final File zipFile, final String encording ) { 102 List<File> list = new ArrayList<File>(); 103 104 // 解凍先フォルダの末尾が'/'又は'\'でなければ区切り文字を挿入 105 // String tmpPrefix = targetPath; 106 // if( File.separatorChar != targetPath.charAt( targetPath.length() - 1 ) ) { 107 // tmpPrefix = tmpPrefix + File.separator; 108 // } 109 110 ZipArchiveInputStream zis = null; 111 File tmpFile = null; 112 // String fileName = null; 113 114 try { 115 zis = new ZipArchiveInputStream( new BufferedInputStream( new FileInputStream( zipFile ) ) ,encording ); 116 117 ZipArchiveEntry entry = null; 118 while( ( entry = zis.getNextZipEntry() ) != null ) { 119 // fileName = tmpPrefix + entry.getName().replace( '/', File.separatorChar ); 120 tmpFile = new File( targetPath,entry.getName() ); 121 list.add( tmpFile ); 122 123 // ディレクトリの場合は、自身を含むディレクトリを作成 124 if( entry.isDirectory() ) { 125 if( !tmpFile.exists() && !tmpFile.mkdirs() ) { 126 String errMsg = "ディレクトリ作成に失敗しました。[ファイル名=" + tmpFile + "]"; 127 System.err.println( errMsg ); 128 continue; 129 } 130 } 131 // ファイルの場合は、自身の親となるディレクトリを作成 132 else { 133 // 4.3.3.3 (2008/10/22) 作成する前に存在チェック 134 if( !tmpFile.getParentFile().exists() && !tmpFile.getParentFile().mkdirs() ) { 135 String errMsg = "親ディレクトリ作成に失敗しました。[ファイル名=" + tmpFile + "]"; 136 System.err.println( errMsg ); 137 continue; 138 } 139 140 BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( tmpFile ) ); 141 try { 142 IOUtils.copy( zis,out ); 143 } 144 catch( IOException zex ) { 145 String errMsg = "ZIPファイルの作成(copy)に失敗しました。[ファイル名=" + tmpFile + "]"; 146 System.err.println( errMsg ); 147 continue; 148 } 149 finally { 150 Closer.ioClose( out ); 151 } 152 } 153 // 5.1.9.0 (2010/08/01) 更新時刻の設定 154 long lastTime = entry.getTime(); 155 if( lastTime >= 0 && !tmpFile.setLastModified( lastTime ) ) { 156 String errMsg = "ZIP更新時刻の設定に失敗しました。[ファイル名=" + tmpFile + "]"; 157 System.err.println( errMsg ); 158 } 159 } 160 } 161 catch( FileNotFoundException ex ) { 162 String errMsg = "解凍ファイルが作成できません。[ファイル名=" + tmpFile + "]"; 163 throw new RuntimeException( errMsg, ex ); 164 } 165 catch( IOException ex ) { 166 String errMsg = "ZIPファイルの解凍に失敗しました。[ファイル名=" + tmpFile + "]"; 167 throw new RuntimeException( errMsg, ex ); 168 } 169 finally { 170 Closer.ioClose( zis ); 171 } 172 173 return list; 174 } 175 176 /** 177 * 引数に指定されたファイル又はフィルダ内に存在するファイルをZIPファイルに圧縮します。 178 * 圧縮レベルはデフォルトのDEFAULT_COMPRESSIONです。 179 * 圧縮ファイルのエントリー情報として本来は、圧縮前後のファイルサイズ、変更日時、CRCを登録する 180 * 必要がありますが、ここでは高速化のため、設定していません。(特に圧縮後ファイルサイズの取得は、 181 * 非常に不可がかかる。) 182 * このため、一部のアーカイバでは正しく解凍できない可能性があります。 183 * 既にZIPファイルが存在する場合でも、上書きされますので注意下さい。 184 * 185 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 186 * @og.rev 5.7.1.2 (2013/12/20) org.apache.commons.compress パッケージの利用(日本語ファイル名対応) 187 * 188 * @param files 圧縮対象のファイル配列 189 * @param zipFile ZIPファイル名 190 * 191 * @return ZIPファイルのエントリーファイル名一覧 192 */ 193 public static List<File> compress( final File[] files, final File zipFile ) { 194 return compress( files,zipFile,"Windows-31J" ); 195 } 196 197 /** 198 * 引数に指定されたファイル又はフィルダ内に存在するファイルをZIPファイルに圧縮します。 199 * 圧縮レベルはデフォルトのDEFAULT_COMPRESSIONです。 200 * 圧縮ファイルのエントリー情報として本来は、圧縮前後のファイルサイズ、変更日時、CRCを登録する 201 * 必要がありますが、ここでは高速化のため、設定していません。(特に圧縮後ファイルサイズの取得は、 202 * 非常に不可がかかる。) 203 * このため、一部のアーカイバでは正しく解凍できない可能性があります。 204 * 既にZIPファイルが存在する場合でも、上書きされますので注意下さい。 205 * 206 * @og.rev 5.7.1.2 (2013/12/20) org.apache.commons.compress パッケージの利用(日本語ファイル名対応) 207 * 208 * @param dir 圧縮対象のディレクトリか、ファイル 209 * @param zipFile ZIPファイル名 210 * 211 * @return ZIPファイルのエントリーファイル名一覧 212 */ 213 public static List<File> compress( final File dir, final File zipFile ) { 214 File[] files = null; 215 if( dir.isDirectory() ) { files = dir.listFiles(); } 216 else { files = new File[] { dir } ; } // 単独の場合は、配列にして渡します。 217 218 return compress( files,zipFile,"Windows-31J" ); 219 } 220 221 /** 222 * 引数に指定されたファイル又はフィルダ内に存在するファイルをZIPファイルに圧縮します。 223 * 圧縮レベルはデフォルトのDEFAULT_COMPRESSIONです。 224 * 圧縮ファイルのエントリー情報として本来は、圧縮前後のファイルサイズ、変更日時、CRCを登録する 225 * 必要がありますが、ここでは高速化のため、設定していません。(特に圧縮後ファイルサイズの取得は、 226 * 非常に不可がかかる。) 227 * このため、一部のアーカイバでは正しく解凍できない可能性があります。 228 * 既にZIPファイルが存在する場合でも、上書きされますので注意下さい。 229 * 230 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 231 * @og.rev 5.7.1.2 (2013/12/20) org.apache.commons.compress パッケージの利用(日本語ファイル名対応) 232 * 233 * @param files 圧縮対象のファイル配列 234 * @param zipFile ZIPファイル名 235 * @param encording ファイルのエンコード(Windows環境では、"Windows-31J" を指定します) 236 * 237 * @return ZIPファイルのエントリーファイル名一覧 238 */ 239 public static List<File> compress( final File[] files, final File zipFile, final String encording ) { 240 List<File> list = new ArrayList<File>(); 241 ZipArchiveOutputStream zos = null; 242 243 try { 244 zos = new ZipArchiveOutputStream( new BufferedOutputStream ( new FileOutputStream( zipFile ) ) ); 245 if (encording != null) { 246 zos.setEncoding( encording ); // "Windows-31J" 247 } 248 249 // ZIP圧縮処理を行います 250 addZipEntry( list, zos, "" , files ); // 開始フォルダは、空文字列とします。 251 } 252 catch( FileNotFoundException ex ) { 253 String errMsg = "ZIPファイルが見つかりません。[ファイル名=" + zipFile + "]"; 254 throw new RuntimeException( errMsg, ex ); 255 } 256 finally { 257 Closer.ioClose( zos ); 258 // zos.finish(); 259 // zos.flush(); 260 // zos.close(); 261 } 262 263 return list; 264 } 265 266 /** 267 * ZIP圧縮処理を行います。 268 * 引数に指定されたFileオブジェクトがディレクトリであれば再帰的に呼び出し、 269 * 下層のファイルをエントリーします。但し、そのディレクトリ自身が空である場合は、 270 * ディレクトリをエントリー情報として設定します。 271 * 272 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 273 * @og.rev 5.1.9.0 (2010/08/01) 更新時刻の設定 、BufferedInputStream のスコープを小さくする。 274 * 275 * @param list ZIPファイルのエントリーファイル名一覧 276 * @param zos ZIP用OutputStream 277 * @param prefix 圧縮時のフォルダ 278 * @param files 圧縮対象のファイル配列 279 * @throws IOException 入出力エラーが発生した場合 280 */ 281 private static void addZipEntry( final List<File> list, final ZipArchiveOutputStream zos, final String prefix, final File[] files ) { 282 File tmpFile = null; 283 try { 284 for( File fi : files ) { 285 tmpFile = fi; // エラー時のファイル 286 list.add( fi ); 287 if( fi.isDirectory() ) { 288 String entryName = prefix + fi.getName() + "/" ; 289 ZipArchiveEntry zae = new ZipArchiveEntry( entryName ); 290 zos.putArchiveEntry( zae ); 291 zos.closeArchiveEntry(); 292 293 addZipEntry( list, zos, entryName, fi.listFiles() ); 294 } 295 else { 296 String entryName = prefix + fi.getName() ; 297 ZipArchiveEntry zae = new ZipArchiveEntry( entryName ); 298 zos.putArchiveEntry( zae ); 299 InputStream is = new BufferedInputStream( new FileInputStream(fi) ); 300 IOUtils.copy( is,zos ); 301 zos.closeArchiveEntry(); 302 Closer.ioClose( is ); 303 } 304 } 305 } 306 catch( FileNotFoundException ex ) { 307 String errMsg = "圧縮対象のファイルが見つかりません。[ファイル名=" + tmpFile + "]"; 308 throw new RuntimeException( errMsg, ex ); 309 } 310 catch( IOException ex ) { 311 String errMsg = "ZIP圧縮に失敗しました。[ファイル名=" + tmpFile + "]"; 312 throw new RuntimeException( errMsg, ex ); 313 } 314 } 315 316 /** 317 * ファイルの圧縮または解凍を行います。 318 * 319 * @og.rev 4.1.0.2 (2008/02/01) 新規追加 320 * 321 * Usage: java org.opengion.fukurou.util.ZipArchive comp|uncomp targetPath zipFileName 322 * 第1引数 : comp:圧縮 uncomp:解凍 323 * 第2引数 : ZIPファイル名 324 * 第3引数 : 圧縮時:圧縮対象のファイル又はフォルダ 解凍時:解凍先のフォルダ 325 * 326 * @param args パラメータ 327 */ 328 public static void main( final String[] args ) { 329 String usage = "Usage: java org.opengion.fukurou.util.ZipArchive comp|uncomp targetPath zipFileName"; 330 if( args.length < 3 ) { 331 System.out.println( usage ); 332 return; 333 } 334 335 // 開始時間 336 long start = System.currentTimeMillis(); 337 338 List<File> list = null; 339 File tgtFile = new File(args[1]); 340 File zipFile = new File(args[2]); 341 if( "comp".equalsIgnoreCase( args[0] ) ) { 342 list = compress( tgtFile, zipFile ); 343 } 344 else if( "uncomp".equalsIgnoreCase( args[0] ) ) { 345 list = unCompress( tgtFile, zipFile ); 346 } 347 else { 348 System.out.println( usage ); 349 return; 350 } 351 352 if( list != null ) { 353 // 処理時間を表示 354 System.out.println( "処理時間 : " + ( System.currentTimeMillis() - start ) + "(ms)" ); 355 // 結果を表示 356 for( File fileName : list ) { 357 System.out.println( fileName ); 358 } 359 } 360 } 361}