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.BufferedReader; 021import java.io.BufferedWriter; 022import java.io.File; 023import java.io.InputStream; 024import java.io.FileInputStream; 025import java.io.FileNotFoundException; 026import java.io.FileOutputStream; 027import java.io.IOException; 028import java.io.InputStreamReader; 029import java.io.OutputStream; 030import java.io.OutputStreamWriter; 031import java.io.PrintWriter; 032import java.io.UnsupportedEncodingException; 033import java.io.Writer; 034import java.util.Collections; 035import java.util.List; 036 037// import java.nio.ByteBuffer; 038import java.nio.channels.FileChannel; 039 040/** 041 * FileUtil.java は、共通的に使用される File関連メソッドを集約した、クラスです。 042 * 043 * 全変数は、public static final 宣言されており、全メソッドは、public static synchronized 宣言されています。 044 * 045 * @og.group ユーティリティ 046 * 047 * @version 4.0 048 * @author Kazuhiko Hasegawa 049 * @since JDK5.0, 050 */ 051public final class FileUtil { 052 private static final NonClosePrintWriter outWriter = new NonClosePrintWriter( System.out ); 053 private static final NonClosePrintWriter errWriter = new NonClosePrintWriter( System.err ); 054 055 /** 056 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。 057 * 058 */ 059 private FileUtil() {} 060 061 /** システム依存の改行記号をセットします。 */ 062 private static final String CR = System.getProperty("line.separator"); 063 064 /** 5.6.1.2 (2013/02/22) UNIX系のファイル名を表すセパレータ文字 */ 065 private static final char UNIX_SEPARATOR = '/'; 066 067 /** 5.6.1.2 (2013/02/22) Windwos系のファイル名を表すセパレータ文字 */ 068 private static final char WINDOWS_SEPARATOR = '\\'; 069 070 /** 5.6.1.2 (2013/02/22) ファイルの拡張子の区切りを表す文字 */ 071 public static final char EXTENSION_SEPARATOR = '.'; 072 073 /** 074 * Fileオブジェクトとエンコードより PrintWriterオブジェクトを作成します。 075 * 076 * @param file 出力するファイルオブジェクト 077 * @param encode ファイルのエンコード 078 * 079 * @return PrintWriterオブジェクト 080 * @throws RuntimeException 何らかのエラーが発生した場合 081 */ 082 public static PrintWriter getPrintWriter( final File file,final String encode ) { 083 return getPrintWriter( file,encode,false ); 084 } 085 086 /** 087 * Fileオブジェクトとエンコードより PrintWriterオブジェクトを作成します。 088 * 089 * @param file 出力するファイルオブジェクト 090 * @param encode ファイルのエンコード 091 * @param append ファイルを追加モード(true)にするかどうか 092 * 093 * @return PrintWriterオブジェクト 094 * @throws RuntimeException 何らかのエラーが発生した場合 095 */ 096 public static PrintWriter getPrintWriter( final File file,final String encode,final boolean append ) { 097 final PrintWriter writer ; 098 099 try { 100 writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 101 new FileOutputStream(file,append) ,encode ))); 102 } 103 catch( UnsupportedEncodingException ex ) { 104 String errMsg = "指定されたエンコーディングがサポートされていません。" + CR 105 + ex.getMessage() + CR 106 + "File=[" + file + " , encode=[" + encode + "]" ; 107 throw new RuntimeException( errMsg,ex ); 108 } 109 catch( FileNotFoundException ex ) { // 3.6.1.0 (2005/01/05) 110 String errMsg = "ファイル名がオープン出来ませんでした。" + CR 111 + ex.getMessage() + CR 112 + "File=[" + file + " , encode=[" + encode + "]" ; 113 throw new RuntimeException( errMsg,ex ); 114 } 115 116 return writer ; 117 } 118 119 /** 120 * ファイル名より、PrintWriterオブジェクトを作成する簡易メソッドです。 121 * 122 * これは、ファイル名は、フルパスで、追加モードで、UTF-8 エンコードの 123 * ログファイルを出力する場合に使用します。 124 * また、ファイル名に、"System.out" と、"System.err" を指定できます。 125 * その場合は、標準出力、または、標準エラー出力に出力されます。 126 * "System.out" と、"System.err" を指定した場合は、NonClosePrintWriter 127 * オブジェクトが返されます。これは、close() 処理が呼ばれても、何もしない 128 * クラスです。また、常に内部キャッシュの同じオブジェクトが返されます。 129 * 130 * @param file 出力するファイル名 131 * 132 * @return PrintWriterオブジェクト 133 * @throws RuntimeException 何らかのエラーが発生した場合 134 * @throws IllegalArgumentException ファイル名が null の場合 135 */ 136 public static PrintWriter getLogWriter( final String file ) { 137 if( file == null ) { 138 String errMsg = "ファイル名に、null は指定できません。"; 139 throw new IllegalArgumentException( errMsg ); 140 } 141 142 final PrintWriter writer ; 143 if( "System.out".equalsIgnoreCase( file ) ) { 144 writer = outWriter ; 145 } 146 else if( "System.err".equalsIgnoreCase( file ) ) { 147 writer = errWriter ; 148 } 149 else { 150 writer = getPrintWriter( new File( file ),"UTF-8",true ); 151 } 152 153 return writer ; 154 } 155 156 /** 157 * OutputStreamとエンコードより PrintWriterオブジェクトを作成します。 158 * 159 * @og.rev 5.5.2.0 (2012/05/01) 新規追加 160 * 161 * @param os 利用するOutputStream 162 * @param encode ファイルのエンコード 163 * 164 * @return PrintWriterオブジェクト 165 * @throws RuntimeException 何らかのエラーが発生した場合 166 */ 167 public static PrintWriter getPrintWriter( final OutputStream os,final String encode ) { 168 final PrintWriter writer ; 169 170 try { 171 writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 172 os ,encode ))); 173 } 174 catch( UnsupportedEncodingException ex ) { 175 String errMsg = "指定されたエンコーディングがサポートされていません。" + CR 176 + ex.getMessage() + CR 177 + "encode=[" + encode + "]" ; 178 throw new RuntimeException( errMsg,ex ); 179 } 180 return writer ; 181 } 182 183 /** 184 * PrintWriter を継承した、JspWriterなどの Writer 用のクラスを定義します。 185 * 186 * 例えば、JspWriterなどの JSP/Servlet等のフレームワークで使用される 187 * Writer では、flush や close 処理は、フレームワーク内で行われます。 188 * その場合、通常のファイルと同じ用に、flush や close をアプリケーション側で 189 * 行うと、内部処理的に不整合が発生したり、最悪の場合エラーになります。 190 * このクラスは、NonFlushPrintWriter クラスのオブジェクトを返します。 191 * これは、通常の、new PrintWriter( Writer ) で、求めるのと、ほとんど同様の 192 * 処理を行いますが、close() と flush() メソッドが呼ばれても、何もしません。 193 * 194 * @param writer 出力するWriteオブジェクト(NonFlushPrintWriterクラス) 195 * 196 * @return PrintWriterオブジェクト 197 */ 198 public static PrintWriter getNonFlushPrintWriter( final Writer writer ) { 199 return new NonFlushPrintWriter( writer ); 200 } 201 202 /** 203 * Fileオブジェクトとエンコードより BufferedReaderオブジェクトを作成します。 204 * 205 * @param file 入力するファイルオブジェクト 206 * @param encode ファイルのエンコード 207 * 208 * @return BufferedReaderオブジェクト 209 * @throws RuntimeException 何らかのエラーが発生した場合 210 */ 211 public static BufferedReader getBufferedReader( final File file,final String encode ) { 212 final BufferedReader reader ; 213 214 try { 215 reader = new BufferedReader(new InputStreamReader( 216 new FileInputStream( file ) ,encode )); 217 } 218 catch( UnsupportedEncodingException ex ) { 219 String errMsg = "指定されたエンコーディングがサポートされていません。" + CR 220 + ex.getMessage() + CR 221 + "FIle=[" + file + " , encode=[" + encode + "]" ; 222 throw new RuntimeException( errMsg,ex ); 223 } 224 catch( FileNotFoundException ex ) { 225 String errMsg = "ファイル名がオープン出来ませんでした。" + CR 226 + ex.getMessage() + CR 227 + "FIle=[" + file + " , encode=[" + encode + "]" ; 228 throw new RuntimeException( errMsg,ex ); 229 } 230 231 return reader ; 232 } 233 234 /** 235 * 指定のファイル名が、実際に存在しているかどうかをチェックします。 236 * 存在しない場合は、2秒毎に、3回確認します。 237 * それでも存在しない場合は、エラーを返します。 238 * return されるFileオブジェクトは、正規の形式(CanonicalFile)です。 239 * 240 * @param dir フォルダ名 241 * @param filename ファイル名 242 * 243 * @return 存在チェック(なければ null/あれば、CanonicalFile) 244 */ 245 public static File checkFile( final String dir, final String filename ) { 246 return checkFile( dir,filename,3 ); 247 } 248 249 /** 250 * 指定のファイル名が、実際に存在しているかどうかをチェックします。 251 * 存在しない場合は、2秒毎に、指定の回数分確認します。 252 * それでも存在しない場合は、エラーを返します。 253 * return されるFileオブジェクトは、正規の形式(CanonicalFile)です。 254 * 255 * @param dir フォルダ名 256 * @param filename ファイル名 257 * @param count 回数指定 258 * 259 * @return 存在チェック(なければ null/あれば、CanonicalFile) 260 */ 261 public static File checkFile( final String dir, final String filename,final int count ) { 262 File file = null; 263 264 int cnt = count; 265 while( cnt > 0 ) { 266 file = new File( dir,filename ); 267 if( file.exists() ) { break; } 268 else { 269 if( cnt == 1 ) { return null; } // 残り1回の場合は、2秒待機せずに即抜ける。 270 try { Thread.sleep( 2000 ); } // 2秒待機 271 catch ( InterruptedException ex ) { 272 System.out.println( "InterruptedException" ); 273 } 274 System.out.println(); 275 System.out.print( "CHECK File Error! CNT=" + cnt ); 276 System.out.print( " File=" + file.getAbsolutePath() ); 277 } 278 cnt--; 279 } 280 281 // ファイルの正式パス名の取得 282 try { 283 return file.getCanonicalFile() ; 284 } 285 catch( IOException ex ) { 286 String errMsg = "ファイルの正式パス名が取得できません。[" + file.getAbsolutePath() + "]"; 287 throw new RuntimeException( errMsg,ex ); 288 } 289 } 290 291 /** 292 * ファイルのバイナリコピーを行います。 293 * 294 * copy( File,File,false ) を呼び出します。 295 * 296 * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。 297 * 298 * @param fromFile コピー元ファイル名 299 * @param toFile コピー先ファイル名 300 * 301 * @return バイナリコピーが正常に終了したかどうか[true:成功/false:失敗] 302 * @see #copy( File,File,boolean ) 303 */ 304 public static boolean copy( final String fromFile,final String toFile ) { 305 return copy( new File( fromFile ), new File( toFile ), false ); 306 } 307 308 /** 309 * ファイルのバイナリコピーを行います。 310 * 311 * copy( File,File,boolean ) を呼び出します。 312 * 第3引数の、keepTimeStamp=true で、コピー元のファイルのタイムスタンプを、 313 * コピー先にもセットします。 314 * 315 * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。 316 * 317 * @param fromFile コピー元ファイル名 318 * @param toFile コピー先ファイル名 319 * @param keepTimeStamp タイムスタンプ維持[true/false] 320 * 321 * @return バイナリコピーが正常に終了したかどうか[true:成功/false:失敗] 322 * @see #copy( File,File,boolean ) 323 */ 324// public static boolean copy( final String fromFile,final String toFile,final boolean changeCrLf ) { 325 public static boolean copy( final String fromFile,final String toFile,final boolean keepTimeStamp ) { 326 return copy( new File( fromFile ), new File( toFile ), keepTimeStamp ); 327 } 328 329 /** 330 * ファイルのバイナリコピーを行います。 331 * 332 * copy( File,File,false ) を呼び出します。 333 * 334 * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。 335 * 336 * @param fromFile コピー元ファイル 337 * @param toFile コピー先ファイル 338 * 339 * @return バイナリコピーが正常に終了したかどうか[true:成功/false:失敗] 340 * @see #copy( File,File,boolean ) 341 */ 342 public static boolean copy( final File fromFile,final File toFile ) { 343 return copy( fromFile, toFile, false ); 344 } 345 346 /** 347 * ファイルのバイナリコピーを行います。 348 * 349 * 第3引数の、keepTimeStamp=true で、コピー元のファイルのタイムスタンプを、 350 * コピー先にもセットします。 351 * toFile が、ディレクトリの場合は、fromFile のファイル名をそのままコピーします。 352 * fromFile がディレクトリの場合は、エラーにします。 353 * copyDirectry( File,Fileboolean )を使用してください。(自動処理はしていません) 354 * 355 * @og.rev 5.1.6.0 (2010/05/01) 新規追加 356 * @og.rev 5.6.5.2 (2013/06/21) ByteBufferを利用した方式から、transferTo を使用する方式に変更 357 * @og.rev 5.7.1.2 (2013/12/20) copy先(toFile)のフォルダが存在しなければ、作成します。 358 * 359 * @param fromFile コピー元ファイル 360 * @param toFile コピー先ファイル 361 * @param keepTimeStamp タイムスタンプ維持[true/false] 362 * 363 * @return バイナリコピーが正常に終了したかどうか[true:成功/false:失敗] 364 * @see #copyDirectry( File,File,boolean ) 365 */ 366 public static boolean copy( final File fromFile,final File toFile,final boolean keepTimeStamp ) { 367 FileInputStream inFile = null; 368 FileOutputStream outFile = null; 369 FileChannel fin = null; 370 FileChannel fout = null; 371 372 File tempToFile = toFile ; 373 try { 374 // fromFileが、ディレクトリの場合は、エラー 375 if( fromFile.isDirectory() ) { 376 System.err.println( fromFile + " がディレクトリのため、処理できません。" ); 377 return false; 378 } 379 // toFileが、ディレクトリの場合は、そのパスでファイル名をfromFileから取り出す。 380 if( toFile.isDirectory() ) { 381 tempToFile = new File( toFile,fromFile.getName() ); 382 } 383 384 // 5.7.1.2 (2013/12/20) copy先(toFile)のフォルダが存在しなければ、作成します。 385 File parent = tempToFile.getParentFile(); 386 if( !parent.exists() && !parent.mkdirs() ) { 387 // ディレクトリを作成する 388 System.err.println( parent + " の ディレクトリ作成に失敗しました。" ); 389 return false; 390 } 391 392 inFile = new FileInputStream( fromFile ); 393 outFile = new FileOutputStream( tempToFile ); 394 395 fin = inFile.getChannel(); 396 fout = outFile.getChannel(); 397 398 // 5.6.5.2 (2013/06/21) ByteBufferを利用した方式から、transferTo を使用する方式に変更 399// ByteBuffer buffer = ByteBuffer.allocateDirect( BUFSIZE ); 400// while ( (fin.read(buffer) != -1) || buffer.position() > 0) { 401// buffer.flip(); 402// fout.write( buffer ); 403// buffer.compact(); 404// } 405 406 fin.transferTo(0, fin.size(), fout ); 407 408 } 409 catch ( IOException ex ) { 410 System.out.println(ex.getMessage()); 411 return false; 412 } 413 finally { 414 Closer.ioClose( inFile ) ; 415 Closer.ioClose( outFile ); 416 Closer.ioClose( fin ) ; 417 Closer.ioClose( fout ); 418 } 419 420 if( keepTimeStamp ) { 421 return tempToFile.setLastModified( fromFile.lastModified() ); 422 } 423 424 return true; 425 } 426// public static boolean copy( final File fromFile,final File toFile,final boolean keepTimeStamp ) { 427// BufferedInputStream fromStream = null; 428// BufferedOutputStream toStream = null; 429// File tempToFile = toFile ; 430// try { 431// // fromFileが、ディレクトリの場合は、エラー 432// if( fromFile.isDirectory() ) { 433// System.err.println( fromFile + " がディレクトリのため、処理できません。" ); 434// return false; 435// } 436// // toFileが、ディレクトリの場合は、そのパスでファイル名をfromFileから取り出す。 437// if( toFile.isDirectory() ) { 438// tempToFile = new File( toFile,fromFile.getName() ); 439// } 440// 441// fromStream = new BufferedInputStream( new FileInputStream( fromFile ) ); 442// toStream = new BufferedOutputStream( new FileOutputStream( tempToFile ) ); 443// 444// boolean isOK = copy( fromStream,toStream ); 445// if( !isOK ) { return false; } 446// 447// } 448// catch ( IOException ex ) { 449// System.out.println(ex.getMessage()); 450// return false; 451// } 452// finally { 453// Closer.ioClose( fromStream ) ; 454// Closer.ioClose( toStream ) ; 455// } 456// 457// if( keepTimeStamp ) { 458// tempToFile.setLastModified( fromFile.lastModified() ); 459// } 460// 461// return true; 462// } 463 464 private static final byte B_CR = (byte)0x0d ; // '\r' 465 private static final byte B_LF = (byte)0x0a ; // '\n' 466 private static final int BUFSIZE = 8192 ; // 5.1.6.0 (2010/05/01) 467 468 /** 469 * ファイルのバイナリコピーを行います。 470 * 471 * このファイルコピーは、バイナリファイルの 改行コードを 472 * CR+LF に統一します。また、UTF-8 の BOM(0xef,0xbb,0xbf) があれば、 473 * 取り除きます。 474 * 475 * @og.rev 5.1.6.0 (2010/05/01) 新規追加 476 * 477 * @param fromFile コピー元ファイル 478 * @param toFile コピー先ファイル 479 * 480 * @return バイナリコピーが正常に終了したかどうか[true:成功/false:失敗] 481 */ 482 public static boolean changeCrLfcopy( final File fromFile,final File toFile ) { 483 BufferedInputStream fromStream = null; 484 BufferedOutputStream toStream = null; 485 File tempToFile = toFile ; 486 try { 487 // ディレクトリの場合は、そのパスでファイル名をfromFileから取り出す。 488 if( toFile.isDirectory() ) { 489 tempToFile = new File( toFile,fromFile.getName() ); 490 } 491 fromStream = new BufferedInputStream( new FileInputStream( fromFile ) ); 492 toStream = new BufferedOutputStream( new FileOutputStream( tempToFile ) ); 493 494// int BUFSIZE = 8192 ; // 5.1.6.0 (2010/05/01) static final定義 495 byte[] buf = new byte[BUFSIZE]; 496 int len ; 497 // 4.2.3.0 (2008/05/26) changeCrLf 属性対応 498 499 boolean bomCheck = true; // 最初の一回だけ、BOMチェックを行う。 500 byte bt = (byte)0x00; // バッファの最後と最初の比較時に使用 501 while( (len = fromStream.read(buf,0,BUFSIZE)) != -1 ) { 502 int st = 0; 503 if( bomCheck && len >= 3 && 504 buf[0] == (byte)0xef && 505 buf[1] == (byte)0xbb && 506 buf[2] == (byte)0xbf ) { 507 st = 3; 508 } 509 else { 510 // バッファの最後が CR で、先頭が LF の場合、LF をパスします。 511 if( bt == B_CR && buf[0] == B_LF ) { 512 st = 1 ; 513 } 514 } 515 bomCheck = false; 516 517 for( int i=st;i<len;i++ ) { 518 bt = buf[i] ; 519 if( bt == B_CR || bt == B_LF ) { 520 toStream.write( (int)B_CR ); // CR 521 toStream.write( (int)B_LF ); // LF 522 // CR+LF の場合 523 if( bt == B_CR && i+1 < len && buf[i+1] == B_LF ) { 524 i++; 525 bt = buf[i] ; 526 } 527 } 528 else { 529 toStream.write( (int)bt ); 530 } 531 } 532 } 533 // 最後が改行コードでなければ、改行コードを追加します。 534 // テキストコピーとの互換性のため 535 if( bt != B_CR && bt != B_LF ) { 536 toStream.write( (int)B_CR ); // CR 537 toStream.write( (int)B_LF ); // LF 538 } 539 } 540 catch ( IOException ex ) { 541 System.out.println(ex.getMessage()); 542 return false; 543 } 544 finally { 545 Closer.ioClose( fromStream ) ; 546 Closer.ioClose( toStream ) ; 547 } 548 549 return true; 550 } 551 552 /** 553 * 入出力ストリーム間でデータの転送を行います。 554 * 555 * ここでは、すでに作成されたストリームに基づき、データの入出力を行います。 556 * よって、先にフォルダ作成や、存在チェック、ファイルの削除などの必要な処理は 557 * 済まして置いてください。 558 * また、このメソッド内で、ストリームのクロース処理は行っていません。 559 * 560 * @og.rev 5.1.6.0 (2010/05/01) 新規追加 561 * 562 * @param input 入力ストリーム 563 * @param output 出力ストリーム 564 * 565 * @return データ転送が正常に終了したかどうか[true:成功/false:失敗] 566 */ 567 public static boolean copy( final InputStream input,final OutputStream output ) { 568 if( input == null ) { 569 System.err.println( "入力ストリームが 作成されていません。" ); 570 return false; 571 } 572 573 if( output == null ) { 574 System.err.println( "出力ストリームが 作成されていません。" ); 575 return false; 576 } 577 578 try { 579 byte[] buf = new byte[BUFSIZE]; 580 int len; 581 while((len = input.read(buf)) != -1) { 582 output.write(buf, 0, len); 583 } 584 } 585 catch ( IOException ex ) { 586 System.out.println( ex.getMessage() ); 587 return false; 588 } 589 // finally { 590 // Closer.ioClose( input ); 591 // Closer.ioClose( output ); 592 // } 593 return true ; 594 } 595 596 /** 597 * 再帰処理でディレクトリのコピーを行います。 598 * 599 * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。 600 * 601 * @og.rev 4.3.0.0 (2008/07/24) 追加 602 * @og.rev 5.1.6.0 (2010/05/01) 戻り値に、true/false 指定します。 603 * 604 * @param fromDir コピー元ディレクトリ名 605 * @param toDir コピー先ディレクトリ名 606 * 607 * @return ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗] 608 */ 609 public static boolean copyDirectry( final String fromDir, final String toDir ) { 610 return copyDirectry( new File( fromDir ), new File( toDir ),false ); 611 } 612 613 /** 614 * 再帰処理でディレクトリをコピーします。 615 * 616 * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。 617 * 618 * @og.rev 4.3.0.0 (2008/07/24) 追加 619 * @og.rev 5.1.6.0 (2010/05/01) 内部処理を若干変更します。 620 * 621 * @param fromDir コピー元ディレクトリ 622 * @param toDir コピー先ディレクトリ 623 * 624 * @return ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗] 625 */ 626 public static boolean copyDirectry( final File fromDir, final File toDir ) { 627 return copyDirectry( fromDir, toDir, false ); 628 } 629 630 /** 631 * 再帰処理でディレクトリをコピーします。 632 * 633 * 指定されたコピー元ディレクトリがディレクトリでなかったり存在しないときは falseを返します。 634 * 635 * @og.rev 4.3.0.0 (2008/07/24) 追加 636 * @og.rev 5.1.6.0 (2010/05/01) 内部処理を若干変更します。 637 * @og.rev 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。 638 * 639 * @param fromDir コピー元ディレクトリ 640 * @param toDir コピー先ディレクトリ 641 * @param keepTimeStamp タイムスタンプ維持[true/false] 642 * 643 * @return ディレクトリのコピーが正常に終了したかどうか[true:成功/false:失敗] 644 */ 645 public static boolean copyDirectry( final File fromDir, final File toDir, final boolean keepTimeStamp ) { 646 // コピー元がディレクトリでない場合はfalseを返す 647 // 4.3.4.4 (2009/01/01) 648 if( !fromDir.exists() || !fromDir.isDirectory() ) { 649 System.err.println( fromDir + " が ディレクトリでないか、存在しません。" ); 650 return false; 651 } 652 653 // 4.3.4.4 (2009/01/01) 654 if( !toDir.exists() ) { 655 // ディレクトリを作成する 656 if( !toDir.mkdirs() ) { 657 System.err.println( toDir + " の ディレクトリ作成に失敗しました。" ); 658 return false; 659 } 660 } 661 662 // ディレクトリ内のファイルをすべて取得する 663 File[] files = fromDir.listFiles(); 664 665 // 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラー 666 if( files == null ) { 667 System.err.println( fromDir + " はアクセスできません。" ); 668 return false; 669 } 670 671 // ディレクトリ内のファイルに対しコピー処理を行う 672 boolean flag = true; 673 for( int i = 0; files.length>i; i++ ){ 674 if( files[i].isDirectory() ){ // ディレクトリだった場合は再帰呼び出しを行う 675 flag = copyDirectry( files[i], new File( toDir, files[i].getName()),keepTimeStamp ); 676 } 677 else{ // ファイルだった場合はファイルコピー処理を行う 678 flag = copy( files[i], new File( toDir, files[i].getName()),keepTimeStamp ); 679 } 680 if( !flag ) { return false; } 681 } 682 return true; 683 } 684// public static boolean copyDirectry( final File fromDirectry, final File toDirectry ) { 685// // コピー元がディレクトリでない場合はfalseを返す 686// // 4.3.4.4 (2009/01/01) 687// if( !fromDirectry.exists() || !fromDirectry.isDirectory() ) { return false; } 688// 689// // 4.3.4.4 (2009/01/01) 690// boolean flag = true; 691// if( !toDirectry.exists() ) { 692// // ディレクトリを作成する 693// flag = toDirectry.mkdirs(); 694// if( ! flag ) { System.err.println( toDirectry.getName() + " の ディレクトリ作成に失敗しました。" ); } 695// } 696// 697// // ディレクトリ内のファイルをすべて取得する 698// File[] files = fromDirectry.listFiles(); 699// 700// // ディレクトリ内のファイルに対しコピー処理を行う 701// for( int i = 0; files.length>i; i++ ){ 702// if( files[i].isDirectory() ){ // ディレクトリだった場合は再帰呼び出しを行う 703// copyDirectry( 704// new File( fromDirectry.toString(), files[i].getName() ), 705// new File( toDirectry.toString(), files[i].getName())); 706// } 707// else{ // ファイルだった場合はファイルコピー処理を行う 708// copy( 709// new File( fromDirectry.toString(), files[i].getName() ), 710// new File( toDirectry.toString(), files[i].getName()) ); 711// } 712// } 713// return true; 714// } 715 716 /** 717 * 指定されたファイル及びディレクトを削除します。 718 * ディレクトリの場合はサブフォルダ及びファイルも削除します。 719 * 1つでもファイルの削除に失敗した場合、その時点で処理を中断しfalseを返します。 720 * 721 * @og.rev 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラーを返します。 722 * 723 * @param file 削除ファイル/ディレクトリ 724 * 725 * @return ファイル/ディレクトリの削除に終了したかどうか[true:成功/false:失敗] 726 */ 727 public static boolean deleteFiles( final File file ) { 728 if( file.exists() ) { 729 if( file.isDirectory() ) { 730 File[] list = file.listFiles(); 731 732 // 5.3.7.0 (2011/07/01) フォルダにアクセスできない場合は、エラー 733 if( list == null ) { 734 System.err.println( file + " はアクセスできません。" ); 735 return false; 736 } 737 738 for( int i=0; i<list.length; i++ ) { 739 deleteFiles( list[i] ); 740 } 741 } 742 if( !file.delete() ) { return false; } 743 } 744 return true; 745 } 746 747 /** 748 * 指定されたディレクトリを基点としたファイル名(パスを含む)の一覧を返します。 749 * 750 * @og.rev 4.3.6.6 (2009/05/15) 新規作成 751 * @og.rev 5.4.3.2 (2012/01/06) 引数isCopy追加 752 * 753 * @param dir 基点となるディレクトリ 754 * @param sort ファイル名でソートするか 755 * @param list ファイル名一覧を格納するList 756 * @param isCopy コピー中ファイルを除外するか [true:含む/false:除外] 757 */ 758 public static void getFileList( final File dir, final boolean sort, final List<String> list, boolean isCopy ) { 759 if( list == null ) { return; } 760 if( dir.isFile() ) { 761 // コピー中判定はrenameで行う 762 if( !isCopy && !dir.renameTo( dir ) ){ 763 return; 764 } 765 else{ 766 list.add( dir.getAbsolutePath() ); 767 } 768 } 769 else if( dir.isDirectory() ) { 770 File[] files = dir.listFiles(); 771 for( int i=0; i<files.length; i++ ) { 772 getFileList( files[i], sort, list, isCopy ); 773 } 774 } 775 if( sort ) { 776 Collections.sort( list ); 777 } 778 } 779 780 /** 781 * 指定されたディレクトリを基点としたファイル名(パスを含む)の一覧を返します。 782 * 互換性のため、コピー中ファイルも含みます。 783 * 784 * @og.rev 5.4.3.2 (2012/01/06) コピー中対応のため引数4つを作成する 785 * 786 * @param dir 基点となるディレクトリ 787 * @param sort ファイル名でソートするか 788 * @param list ファイル名一覧を格納するList 789 */ 790 public static void getFileList( final File dir, final boolean sort, final List<String> list ) { 791 getFileList( dir, sort, list, true ); 792 } 793 794 /** 795 * 指定されたファイル名(パスを含む)から、パスも拡張子もないファイル名を返します。 796 * 797 * @og.rev 5.6.1.2 (2013/02/22) 新規作成 798 * 799 * @param filename ファイル名(パスを含む) 800 * @return パスも、拡張子もないファイル名 801 */ 802 public static String getBaseName( final String filename ) { 803 804 if (filename == null) { 805 return null; 806 } 807 808 // セパレータの位置を取得。 809 int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR); 810 int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR); 811 int lastSepPos = Math.max( lastUnixPos , lastWindowsPos ); 812 813 // 拡張子の位置を取得。 814 int extPos = filename.lastIndexOf(EXTENSION_SEPARATOR); 815 if( lastSepPos > extPos ) { extPos = -1; } // 念のため、最後のセパレータより前にある拡張子の区切り文字は無効。 816 817 if( extPos < 0 ) { 818 // SEPARATOR がなければ、lastSepPos + 1 = 0 となり、先頭から取得できる。 819 return filename.substring( lastSepPos + 1 ); 820 } else { 821 return filename.substring( lastSepPos + 1 , extPos ); 822 } 823 } 824 825 /** 826 * ファイルをリネームを行います。 827 * 引数のuseBackup属性を true にすると、toFile が存在した場合、toFile の直下に "_backup" フォルダを 828 * 作成して、toFile + "_" + (現在時刻のLONG値) + "." + (toFileの拡張子) に名前変更します。 829 * useBackup属性を false にすると、toFile が存在した場合、toFile を削除します。 830 * 831 * @og.rev 5.7.1.2 (2013/12/20) 新規追加 832 * 833 * @param fromFile 名前変更する元のファイル 834 * @param toFile 名前変更後のファイル 835 * @param useBackup バックアップを作成するかどうか(true:作成する/false:作成しない) 836 * @return true:正常処理/false:異常処理 837 */ 838 public static boolean renameTo( final File fromFile , final File toFile , final boolean useBackup ) { 839 if( fromFile == null || toFile == null ) { 840 String errMsg = "入力ファイルが null です。" ; 841 System.err.println( errMsg ); 842 return false; 843 } 844 845 // 変更先のファイルが存在した場合の処理。 846 if( toFile.exists() ) { 847 // バックアップ作成する場合 848 if( useBackup ) { 849 File parent = toFile.getParentFile(); // バックアップすべきファイルのフォルダ 850 File backup = new File( parent , "_backup" ); // その直下に、"_backup" フォルダを作成 851 if( !backup.exists() && !backup.mkdirs() ) { 852 String errMsg = "バックアップ処理でbackupフォルダの作成に失敗しました。[" + backup + "]"; 853 System.err.println( errMsg ); 854 return false; 855 } 856 // バックアップファイル名は、元のファイル名(拡張子含む) + "_" + 現在時刻のlong値 + "." + 元のファイルの拡張子 857 String bkupName = toFile.getName(); 858 File toFile2 = new File( parent,bkupName ); // オリジナルの toFile をrename するとまずいので、同名のFileオブジェクトを作成 859 860 bkupName = bkupName + "_" + System.currentTimeMillis() + "." + getExtension( bkupName ) ; 861 File bkupFile = new File( backup,bkupName ); 862 863 if( !toFile2.renameTo( bkupFile ) ) { 864 String errMsg = "バックアップ処理でバックアップファイルをリネームできませんでした。" +CR 865 + " [" + toFile + "] ⇒ [" + bkupFile + "]" ; 866 System.err.println( errMsg ); 867 return false; 868 } 869 } 870 // バックアップ作成しない場合は、削除します。 871 else if( !toFile.delete() ) { 872 String errMsg = "既存のファイル[" + toFile + "]が削除できませんでした。"; 873 System.err.println( errMsg ); 874 return false; 875 } 876 } 877 878 if( !fromFile.renameTo( toFile ) ) { 879 String errMsg = "所定のファイルをリネームできませんでした。" + CR 880 + " [" + fromFile + "] ⇒ [" + toFile + "]" ; 881 System.err.println( errMsg ); 882 return false; 883 } 884 return true; 885 } 886 887 /** 888 * ファイル名から 拡張子を取得します。 889 * 890 * 一番最後に見つかったピリオドから後ろを切り取って返します。 891 * 拡張子の区切り文字(".")がなければ、空文字列を返します。 892 * 893 * @og.rev 5.7.1.2 (2013/12/20) UploadedFileからに移動。若干のロジック変更 894 * 895 * @param fileName ファイル名 896 * @return 拡張子 897 */ 898 public static String getExtension( final String fileName ) { 899 int extPos = fileName.lastIndexOf( EXTENSION_SEPARATOR ); 900 if( extPos >= 0 ) { 901 return fileName.substring( extPos + 1 ); 902 } 903 return ""; 904 } 905 906 /** 907 * PrintWriter を継承した、System.out/System.err 用のクラスを定義します。 908 * 909 * 通常の、new PrintWriter( OutputStream ) で、求めるのと、ほとんど同様の 910 * 処理を行います。 911 * ただ、close() メソッドが呼ばれても、何もしません。 912 * 913 */ 914 private static final class NonClosePrintWriter extends PrintWriter { 915 /** 916 * コンストラクター 917 * 918 * new PrintWriter( OutputStream ) を行います。 919 * 920 * @param out OutputStream 921 */ 922 public NonClosePrintWriter( final OutputStream out ) { 923 super( out ); 924 } 925 926 /** 927 * close() メソッドをオーバーライドします。 928 * 929 * 何もしません。 930 * 931 */ 932 public void close() { 933 // ここでは処理を行いません。 934 } 935 } 936 937 /** 938 * PrintWriter を継承した、JspWriterなどの Writer 用のクラスを定義します。 939 * 940 * 例えば、JspWriterなどの JSP/Servlet等のフレームワークで使用される 941 * Writer では、flush や close 処理は、フレームワーク内で行われます。 942 * その場合、通常のファイルと同じ用に、flush や close をアプリケーション側で 943 * 行うと、内部処理的に不整合が発生したり、最悪の場合エラーになります。 944 * このクラスは、単に、通常の、new PrintWriter( Writer ) で、求めるのと、 945 * ほとんど同様の処理を行います。 946 * ただ、close() と flush() メソッドが呼ばれても、何もしません。 947 * 948 */ 949 private static final class NonFlushPrintWriter extends PrintWriter { 950 /** 951 * コンストラクター 952 * 953 * new PrintWriter( Writer ) を行います。 954 * 955 * @param writer Writer 956 */ 957 public NonFlushPrintWriter( final Writer writer ) { 958 super( writer ); 959 } 960 961 /** 962 * close() メソッドをオーバーライドします。 963 * 964 * 何もしません。 965 * 966 */ 967 public void close() { 968 // ここでは処理を行いません。 969 } 970 971 /** 972 * flush() メソッドをオーバーライドします。 973 * 974 * 何もしません。 975 * 976 */ 977 public void flush() { 978 // ここでは処理を行いません。 979 } 980 } 981 982 /** 983 * ファイルをコピーします。 984 * 985 * 引数に <file1> <file2> [<encode1> <encode2>] を指定します。 986 * file1 を読み込み、file2 にコピーします。コピー前に、file2 は、file2_backup にコピーします。 987 * file1 が、ディレクトリの場合は、ディレクトリごとコピーします。 988 * encode1、encode2 を指定すると、エンコード変換しながらコピーになります。 989 * この場合は、ファイル同士のコピーのみになります。 990 * 991 * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。 992 * @og.rev 5.1.6.0 (2010/05/01) 引数の並び順、処理を変更します。 993 * 994 * @param args 引数配列 file1 file2 [encode1 encode2] 995 * @throws Throwable なんらかのエラーが発生した場合。 996 */ 997 public static void main( final String[] args ) throws Throwable { 998 if( args.length != 2 && args.length != 4 ) { 999 LogWriter.log("Usage: java FileUtil <file1> <file2> [<encode1> <encode2>]" ); 1000 return ; 1001 } 1002 1003 File file1 = new File( args[0] ); 1004 File file2 = new File( args[1] ); 1005 1006 File tempFile = new File( args[1] + "_backup" ); 1007 1008 if( args.length < 3 ) { 1009 if( file1.isDirectory() ) { 1010 FileUtil.copyDirectry( file1, file2, true ); 1011 } 1012 else { 1013 FileUtil.copy( file2,tempFile ); 1014 FileUtil.copy( file1,file2, true ); 1015 } 1016 } 1017 else { 1018 String encode1 = args[2]; 1019 String encode2 = args[3]; 1020 1021 FileUtil.copy( file2,tempFile ); 1022 1023 BufferedReader reader = FileUtil.getBufferedReader( file1 ,encode1 ); 1024 PrintWriter writer = FileUtil.getPrintWriter( file2 ,encode2 ); 1025 1026 try { 1027 String line1; 1028 while((line1 = reader.readLine()) != null) { 1029 writer.println( line1 ); 1030 } 1031 } 1032 finally { 1033 Closer.ioClose( reader ) ; 1034 Closer.ioClose( writer ) ; 1035 } 1036 } 1037 } 1038}