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     */
016    package org.opengion.fukurou.util;
017    
018    import java.io.BufferedInputStream;
019    import java.io.BufferedOutputStream;
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.io.FileNotFoundException;
023    import java.io.FileOutputStream;
024    import java.io.IOException;
025    import java.util.ArrayList;
026    import java.util.List;
027    import java.util.zip.ZipEntry;
028    import java.util.zip.ZipInputStream;
029    import 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     */
040    public 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                                    try {
228                                            in = new BufferedInputStream( new FileInputStream( target.getAbsolutePath() ) );
229                                            byte[] buf = new byte[BUF_SIZE];
230                                            int count;
231                                            while( ( count = in.read( buf, 0, BUF_SIZE ) ) != -1 ) {
232                                                    zos.write( buf, 0, count );
233                                            }
234                                    }
235                                    finally {
236    //                                      in.close();
237                                            Closer.ioClose( in );
238                                    }
239                                    zos.closeEntry();
240                            }
241                    }
242                    catch( FileNotFoundException ex ) {
243                            String errMsg = "圧縮対象のファイルが見つかりません?ファイル?" + target.getName() + "]";
244                            throw new RuntimeException( errMsg, ex );
245                    }
246                    catch( IOException ex ) {
247                            String errMsg = "ZIP圧縮に失敗しました?ファイル?" + target.getName() + "]";
248                            throw new RuntimeException( errMsg, ex );
249                    }
250    //              finally {
251    //                      Closer.ioClose( in );
252    //              }
253            }
254    
255            /**
256             * ファイルの圧縮また?解凍を行います?
257             *
258             * @og.rev 4.1.0.2 (2008/02/01) 新規追?
259             *
260             * 使用方?: java [comp or uncomp] [targetPath] [zipFileName]
261             * 第1引数 : comp:圧縮 uncomp:解?
262             * 第2引数 : 圧縮?圧縮対象のファイル又?フォル?解凍時:解凍?のフォル?
263             * 第3引数 : ZIPファイル?
264             *
265             * @param args パラメータ
266             */
267            public static void main( final String[] args ) {
268                    String usage = "Usage: java [comp or uncomp] [targetPath] [zipFileName]";
269                    if( args.length < 3 ) {
270                            System.out.println( usage );
271                            return;
272                    }
273    
274                    // 開始時?
275                    long start = System.currentTimeMillis();
276    
277                    List<String> list = null;
278                    if( "comp".equals( args[0] ) ) {
279                            list = compress( args[1], args[2] );
280                    }
281                    else if( "uncomp".equals( args[0] ) ) {
282                            list = unCompress( args[1], args[2] );
283                    }
284                    else {
285                            System.out.println( usage );
286                            return;
287                    }
288    
289                    if( list != null ) {
290                            // 結果を表示
291                            for( String fileName : list ) {
292                                    System.out.println( fileName );
293                            }
294                            // 処?間を表示
295    //                      System.out.println( "処??: " + String.valueOf( System.currentTimeMillis() - start ) + "(ms)" );
296                            System.out.println( "処??: " + ( System.currentTimeMillis() - start ) + "(ms)" );
297                    }
298            }
299    }