m2

Java でのファイルコピー

Javaでファイルコピーするにはどうするのか?」という今更な質問を受けたんだけど、なるほど、"ファイルコピー"なんてものは copy(A,B) みたいなメソッドが用意されているハズのもので、ストリームを開いてうんたらかんたらなんて発想はおきないかもしれない。

手っ取り早く実装するには以下を参照にするといい。
http://www.rgagnon.com/javadetails/java-0064.html
特に JDK1.4 以降なら煩わしいバッファ確保なんかが不要な FileChannel を使うのが通。

    public void copyFile(File in, File out) throws Exception {
        FileChannel sourceChannel = new
            FileInputStream(in).getChannel();
        FileChannel destinationChannel = new
            FileOutputStream(out).getChannel();
        sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);
        sourceChannel.close();
        destinationChannel.close();
    }

commons.io が使えるなら FileUtils。使い方はAPIをみればわかるだろう。
FileUtils (IO 1.2 API)
http://jakarta.apache.org/commons/io/api-release/org/apache/commons/io/FileUtils.html


んで、(FileUtils は試してないけど)Java でファイルコピーを実装するとファイルの新規作成となり、作成日時や更新日時は引き継がれない。なので OS(今回はWindows) のコピーコマンドを実行する Java プログラムを書いてみた。

    private static int copy( File in, File out ) throws IOException {
        int rc = 0;
        ArrayList cmd = new ArrayList();
        cmd.add( "copy" );
        cmd.add( "/B" );
        cmd.add( "/Y" );
        cmd.add( in.getAbsolutePath() );
        cmd.add( out.getAbsolutePath() );
        Process p = Runtime.getRuntime().exec( 
            (String[])cmd.toArray( new String[ cmd.size()] )
        );
        try {
            rc = p.waitFor();
        } catch ( InterruptedException ie ) {}
        return rc;
    }

動かしてみて初めて知ったんだけど、これは動作しない。以下、StackTrace。

java.io.IOException: CreateProcess: copy /B /Y C:\temp\aaa.txt C:\temp\aaa1.txt error=2
      at java.lang.Win32Process.create(Native Method)
      at java.lang.Win32Process.(Win32Process.java:87)
      at java.lang.Runtime.execInternal(Native Method)
      at java.lang.Runtime.exec(Runtime.java:582)
      at java.lang.Runtime.exec(Runtime.java:505)
      at java.lang.Runtime.exec(Runtime.java:471)
      at Test.copy(Test.java:91)
      at Test.main(Test.java:14)
Exception in thread "main" 

当然、コマンドプロンプトからは実行できるので少しハマった。
結論を先に書くと copy コマンド を cmd.exe 経由で実行するとうまくいく。

    private static int copy( File in, File out ) throws IOException {
        int rc = 0;
        ArrayList cmd = new ArrayList();
        cmd.add( "cmd" );
        cmd.add( "/C" );
        cmd.add( "copy" );
        cmd.add( "/B" );
        cmd.add( "/Y" );
        cmd.add( in.getAbsolutePath() );
        cmd.add( out.getAbsolutePath() );
        Process p = Runtime.getRuntime().exec( 
            (String[])cmd.toArray( new String[ cmd.size()] )
        );
        try {
            rc = p.waitFor();
        } catch ( InterruptedException ie ) {}
        return rc;
    }

情報は以下から。
Java 逆引きAPIリファレンス - dirやcopyなどのシェルコマンドを実行したい

http://www.stackasterisk.jp/tech/javaClassref/java_ref_01_04.jsp#execStringOnly
じつはあんまりよくわかってないんだけど、コマンドプロンプトで copy ってやるのは cmd /c copy と等価なんだろうね。シェル-ビルトインなコマンドはシェル経由じゃないと動かせないのは納得。
UNIX ではどうですかね? 「cp」で動かせますか?