Javaでファイルをコピーする標準的な簡潔な方法は?


421

Javaでファイルをコピーする唯一の方法は、ストリームを開く、バッファを宣言する、1つのファイルを読み取る、それをループして、他のストリームに書き込むことだけであることに常に悩まされてきました。Webには、このタイプのソリューションの同様の、まだわずかに異なる実装が散らばっています。

Java言語の範囲内に留まるより良い方法はありますか(OS固有のコマンドの実行を含まないことを意味します)?おそらくいくつかの信頼できるオープンソースユーティリティパッケージでは、少なくともこの基礎となる実装が不明瞭になり、1行のソリューションが提供されますか?


5
Apache Commons FileUtils、特にcopyFileメソッドに何かがある可能性があります。
ツールキット

22
@GlenBestによって推奨されているJava 7を使用している場合、代わりにFiles.copyを使用します。stackoverflow.com/a/16600787/44737
ロブ・

回答:


274

ツールキットで前述したように、Apache Commons IOは、特にFileUtilsを使用する方法です。copyFile() ; それはあなたのためにすべての重い物を処理します。

また、ポストスクリプトとして、FileUtilsの最近のバージョン(2.0.1リリースなど)では、ファイルのコピーにNIOの使用が追加されています。NIOルーチンは、Javaレイヤーを介してバイトの読み取りと書き込みを行うのではなく、OS /ファイルシステムへの直接コピーを延期するため、NIOはファイルコピーのパフォーマンスを大幅に向上させることができます。したがって、パフォーマンスを求めている場合は、FileUtilsの最新バージョンを使用していることを確認することをお勧めします。


1
非常に役立つ-公式リリースにこれらのnioの変更がいつ組み込まれるかについて何か洞察がありますか?
Peter

2
Apache Commons IOの公開リリースはまだ1.4、grrrrrrr
Peter

14
2010年12月の時点で、Apache Commons IOは2.0.1で、NIO機能を備えています。回答を更新しました。
Simon Nickerson

4
Android
ユーザー

18
@GlenBestによって提案されたJava 7以降を使用している場合は、Files.copyを使用することができます。stackoverflow.com/a/16600787/44737
ロブ・

278

Apache CommonsのようなメガAPIの使用は避けます。これは単純な操作であり、新しいNIOパッケージのJDKに組み込まれています。これは、以前の回答ですでにリンクされていましたが、NIO APIの主要なメソッドは、新しい関数 "transferTo"および "transferFrom"です。

http://java.sun.com/javase/6/docs/api/java/nio/channels/FileChannel.html#transferTo(long,%20long,%20java.nio.channels.WritableByteChannel)

リンクされた記事の1つは、transferFromを使用してこの関数をコードに統合する方法についての優れた方法を示しています。

public static void copyFile(File sourceFile, File destFile) throws IOException {
    if(!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom(source, 0, source.size());
    }
    finally {
        if(source != null) {
            source.close();
        }
        if(destination != null) {
            destination.close();
        }
    }
}

NIOの学習は少しトリッキーになる可能性があるため、このメカニズムを信頼して、一夜にしてNIOを学習しようとする場合があります。個人的な経験から、経験がなく、java.ioストリームを介してIOに紹介された場合、それを学ぶのは非常に難しいことがあります。


2
ありがとう、役立つ情報。私はまだ、Apache Commonsのようなものについて、特にそれが下で(適切に)nioを使用している場合は主張します。しかし、根本的な基礎を理解することが重要であることに同意します。
ピーター

1
残念ながら、注意点があります。Windows 7、32ビットで1.5 Gbファイルをコピーすると、ファイルのマッピングに失敗しました。私は別の解決策を探す必要がありました。
Anton K.

15
上記のコードで考えられる3つの問題:(a)getChannelが例外をスローすると、開いているストリームがリークする可能性があります。(b)大きなファイルの場合、OSが処理できる以上の転送を同時に試みている可能性があります。(c)transferFromの戻り値を無視しているため、ファイルの一部のみをコピーしている可能性があります。これが、org.apache.tools.ant.util.ResourceUtils.copyResourceが非常に複雑な理由です。また、transferFromはOKですが、transferToはLinux上のJDK 1.4で中断します:bugs.sun.com/bugdatabase/view_bug.do?bug_id
Jesse Glick

7
この更新されたバージョンはこれらの懸念に対処していると思います:gist.github.com/889747
Mark Renouf

11
このコードには大きな問題があります。transferTo()はループで呼び出す必要があります。要求された金額全体を転送することは保証されません。
ローン侯爵、2013年

180

Java 7では、次のtry-with-resource構文を使用できます。

public static void copyFile( File from, File to ) throws IOException {

    if ( !to.exists() ) { to.createNewFile(); }

    try (
        FileChannel in = new FileInputStream( from ).getChannel();
        FileChannel out = new FileOutputStream( to ).getChannel() ) {

        out.transferFrom( in, 0, in.size() );
    }
}

あるいは、Java 7で導入された新しいFilesクラスを使用してこれを実現することもできます。

public static void copyFile( File from, File to ) throws IOException {
    Files.copy( from.toPath(), to.toPath() );
}

かなりおしゃれですよね?


15
今日、Javaがこのようなものを追加していないのは驚くべきことです。特定の操作は、コンピューターソフトウェアを作成するための絶対不可欠なものです。JavaのOracle開発者は、オペレーティングシステムから1つまたは2つのことを学び、提供するサービスを調べて、初心者が移行しやすくすることができます。
Rick Hodgin、2011年

2
ああありがとう!ヘルパー関数のすべてを備えた新しい "Files"クラスを知りませんでした。それはまさに私が必要とするものを持っています。例をありがとう。
ChrisCantrell 2012年

1
パフォーマンスに関しては、Java NIO FileChannelの方が優れています。この記事を
Pankaj

5
このコードには大きな問題があります。transferTo()はループで呼び出す必要があります。要求された金額全体を転送することは保証されません。
ローン侯爵、2013年

@スコット:ピートが一行の解決策を求め、あなたはとても近いです... copyFileメソッドでFiles.copyをラップする必要はありません。答えの最初にFiles.copy(Path from、Path to)を置くだけで、既存のFileオブジェクトがある場合はFile.toPath()を使用できることを述べます:Files.copy(fromFile.toPath()、 toFile.toPath())
ロブ・

89
  • これらのメソッドは、パフォーマンスエンジニアリングされています(オペレーティングシステムのネイティブI / Oと統合されています)。
  • これらのメソッドは、ファイル、ディレクトリ、およびリンクで機能します。
  • 提供された各オプションは省略できます-オプションです。

ユーティリティクラス

package com.yourcompany.nio;

class Files {

    static int copyRecursive(Path source, Path target, boolean prompt, CopyOptions options...) {
        CopyVisitor copyVisitor = new CopyVisitor(source, target, options).copy();
        EnumSet<FileVisitOption> fileVisitOpts;
        if (Arrays.toList(options).contains(java.nio.file.LinkOption.NOFOLLOW_LINKS) {
            fileVisitOpts = EnumSet.noneOf(FileVisitOption.class) 
        } else {
            fileVisitOpts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
        }
        Files.walkFileTree(source[i], fileVisitOpts, Integer.MAX_VALUE, copyVisitor);
    }

    private class CopyVisitor implements FileVisitor<Path>  {
        final Path source;
        final Path target;
        final CopyOptions[] options;

        CopyVisitor(Path source, Path target, CopyOptions options...) {
             this.source = source;  this.target = target;  this.options = options;
        };

        @Override
        FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        // before visiting entries in a directory we copy the directory
        // (okay if directory already exists).
        Path newdir = target.resolve(source.relativize(dir));
        try {
            Files.copy(dir, newdir, options);
        } catch (FileAlreadyExistsException x) {
            // ignore
        } catch (IOException x) {
            System.err.format("Unable to create: %s: %s%n", newdir, x);
            return SKIP_SUBTREE;
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        Path newfile= target.resolve(source.relativize(file));
        try {
            Files.copy(file, newfile, options);
        } catch (IOException x) {
            System.err.format("Unable to copy: %s: %s%n", source, x);
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
        // fix up modification time of directory when done
        if (exc == null && Arrays.toList(options).contains(COPY_ATTRIBUTES)) {
            Path newdir = target.resolve(source.relativize(dir));
            try {
                FileTime time = Files.getLastModifiedTime(dir);
                Files.setLastModifiedTime(newdir, time);
            } catch (IOException x) {
                System.err.format("Unable to copy all attributes to: %s: %s%n", newdir, x);
            }
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) {
        if (exc instanceof FileSystemLoopException) {
            System.err.println("cycle detected: " + file);
        } else {
            System.err.format("Unable to copy: %s: %s%n", file, exc);
        }
        return CONTINUE;
    }
}

ディレクトリまたはファイルのコピー

long bytes = java.nio.file.Files.copy( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES,
                 java.nio.file.LinkOption.NOFOLLOW_LINKS);

ディレクトリまたはファイルを移動する

long bytes = java.nio.file.Files.move( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.ATOMIC_MOVE,
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING);

ディレクトリまたはファイルを再帰的にコピーする

long bytes = com.yourcompany.nio.Files.copyRecursive( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES
                 java.nio.file.LinkOption.NOFOLLOW_LINKS );

ファイルのパッケージ名が間違っていました(java.nioではなくjava.nio.fileである必要があります)。そのための編集を送信しました。よろしくお願いします!
Stuart Rossiter 14

43

Java 7では簡単です...

File src = new File("original.txt");
File target = new File("copy.txt");

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);

1
あなたの答えはスコットまたはグレンに何を追加しますか?
Uri Agassi

11
簡潔ですが、少ないほど良いです。彼らの答えは良くて詳細ですが、よく見たときに見逃しました。残念ながらこれにはたくさんの答えがあり、それらの多くは長く、時代遅れで複雑であり、スコットとグレンの良い答えはその中で失われました(私はそれを助けるために賛成票を差し上げます)。私の回答は、exists()とエラーメッセージを削除して3行に減らすことで改善されるのではないかと思います。
Kevin Sadler

これはディレクトリに対しては機能しません。くそー誰もがこれを間違っています。API通信が増えると、障害が発生します。私も間違えました。
mmm

2
@momo問題はファイルをコピーする方法でした。
Kevin Sadler

28

ファイルをコピーして宛先パスに保存するには、以下の方法を使用できます。

public void copy(File src, File dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}

1
これは機能しますが、ここの他の回答よりも優れているとは思いませんか?
2013年

2
@Rupこれは、(a)機能するため、および(b)サードパーティのソフトウェアに依存しないため、他の回答よりもはるかに優れています。
ローン侯爵2014

1
@EJP大丈夫ですが、それほどスマートではありません。ファイルのコピーは、アプリケーションの操作ではなく、OSまたはファイルシステムの操作である必要があります。Javaは、ファイルを明示的に読み取ってそれを停止している場合を除き、コピーを見つけてOSの操作に変換できると期待しています。Javaがそれを実行できないと思われる場合、1Kの読み取りと書き込みをより大きなブロックに最適化すると信頼できますか?また、ソースと宛先が低速ネットワーク上のリモート共有にある場合、これは明らかに不要な作業を行っています。はい、いくつかのサードパーティのJARはばかげて大きい(Guava!)が、適切に行われるこのような多くのものが追加されます。
Rup

魅力のように働いた。サードパーティのライブラリを必要とせず、Java 1.6で動作する最適なソリューション。ありがとう。
James Wierzba、2015

@Rupこれはオペレーティングシステムの機能であることに同意しますが、あなたのコメントには他の意味がありません。最初のコロンの後の部分には、どこかに動詞がありません。私はJavaが1kのブロックをより大きなものに変換することを期待することも「信頼すること」もしませんが、私自身は確かにはるかに大きなブロックを使用します。そもそも共有ファイルを使用するアプリケーションを書くことは決してしませんでした。サードパーティのライブラリが、おそらくより大きなバッファを使用することを除いて、このコードよりも「適切」なことを行っていることを私は知りません。
ローン侯爵、

24

これらのメカニズムはすべて、ファイルの内容のみをコピーし、権限などのメタデータはコピーしないことに注意してください。したがって、Linuxで実行可能な.shファイルをコピーまたは移動すると、新しいファイルは実行できなくなります。

本当にファイルをコピーまたは移動するには、つまりコマンドラインからコピーした場合と同じ結果を得るには、実際にはネイティブツールを使用する必要があります。シェルスクリプトまたはJNI。

どうやら、これはjava 7- http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.htmlで修正される可能性があります。成功を祈っている!


23

GoogleのGuavaライブラリにもcopyメソッドがあります

public static void copyFile  from、
                         File  to)IOExceptionを 
                 スローします
すべてのバイトを1つのファイルから別のファイルにコピーします。

警告:がto既存のファイルを表す場合、そのファイルはの内容で上書きされますfrom。場合tofromを参照して、同じファイル、そのファイルの内容が削除されます。

パラメータ:from -ソースファイルto-デスティネーションファイル

スロー: IOException -I / Oエラーが発生した場合 IllegalArgumentException-from.equals(to)



7

上記のコードで考えられる3つの問題:

  1. getChannelが例外をスローすると、開いているストリームがリークする可能性があります。
  2. 大きなファイルの場合、OSが処理できる以上の量を一度に転送しようとしている可能性があります。
  3. transferFromの戻り値を無視しているため、ファイルの一部のみをコピーしている可能性があります。

これがorg.apache.tools.ant.util.ResourceUtils.copyResourceとても複雑な理由です。また、transferFromは問題ありませんが、transferToはLinux上のJDK 1.4で中断しますバグID:5056395を参照)– Jesse Glick Jan


7

すでにSpringを使用しているWebアプリケーションを使用していて、単純なファイルコピーにApache Commons IOを含めたくない場合は、SpringフレームワークのFileCopyUtilsを使用できます。


7

1行のコードでファイルを簡単にコピーできる3つの方法を次に示します。

Java7

java.nio.file.Files#copy

private static void copyFileUsingJava7Files(File source, File dest) throws IOException {
    Files.copy(source.toPath(), dest.toPath());
}

Appache Commons IO

FileUtils#copyFile

private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
    FileUtils.copyFile(source, dest);
}

グアバ

Files#copy

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}

最初のものはディレクトリに対しては機能しません。くそー誰もがこれを間違っています。API通信が増えると、障害が発生します。私も間違えました。
mmm '17年

最初のものは3つのパラメータが必要です。Files.copy唯一の2つのパラメータを使用することのためであるPathStream。ただ、パラメータを追加StandardCopyOption.COPY_ATTRIBUTESまたはStandardCopyOption.REPLACE_EXISTINGのためPathPath
ポンTrizkit

6
public static void copyFile(File src, File dst) throws IOException
{
    long p = 0, dp, size;
    FileChannel in = null, out = null;

    try
    {
        if (!dst.exists()) dst.createNewFile();

        in = new FileInputStream(src).getChannel();
        out = new FileOutputStream(dst).getChannel();
        size = in.size();

        while ((dp = out.transferFrom(in, p, size)) > 0)
        {
            p += dp;
        }
    }
    finally {
        try
        {
            if (out != null) out.close();
        }
        finally {
            if (in != null) in.close();
        }
    }
}

それで、トップの受け入れられた答えとの違いは、whileループでtransferFromを持っているということですか?
Rup

1
コンパイルさえしません、そしてcreateNewFile()呼び出しは冗長で無駄です。
ローン侯爵14

3

私のテストによると、バッファを使用したNIOコピーが最速です。https://github.com/mhisoft/fastcopyにある私のテストプロジェクトから以下の作業コードを参照してください。

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;


public class test {

private static final int BUFFER = 4096*16;
static final DecimalFormat df = new DecimalFormat("#,###.##");
public static void nioBufferCopy(final File source, final File target )  {
    FileChannel in = null;
    FileChannel out = null;
    double  size=0;
    long overallT1 =  System.currentTimeMillis();

    try {
        in = new FileInputStream(source).getChannel();
        out = new FileOutputStream(target).getChannel();
        size = in.size();
        double size2InKB = size / 1024 ;
        ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER);

        while (in.read(buffer) != -1) {
            buffer.flip();

            while(buffer.hasRemaining()){
                out.write(buffer);
            }

            buffer.clear();
        }
        long overallT2 =  System.currentTimeMillis();
        System.out.println(String.format("Copied %s KB in %s millisecs", df.format(size2InKB),  (overallT2 - overallT1)));
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    finally {
        close(in);
        close(out);
    }
}

private static void close(Closeable closable)  {
    if (closable != null) {
        try {
            closable.close();
        } catch (IOException e) {
            if (FastCopy.debug)
                e.printStackTrace();
        }    
    }
}

}


いいね!これは標準のjava.ioストリームではなく高速です.. 160秒で10GBのみをコピー
aswzen

2

高速で、JavaのすべてのバージョンとAndroidでも動作します。

private void copy(final File f1, final File f2) throws IOException {
    f2.createNewFile();

    final RandomAccessFile file1 = new RandomAccessFile(f1, "r");
    final RandomAccessFile file2 = new RandomAccessFile(f2, "rw");

    file2.getChannel().write(file1.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f1.length()));

    file1.close();
    file2.close();
}

1
ただし、すべてのファイルシステムがメモリマップファイルをサポートしているわけではありません。小さなファイルの場合は、比較的コストがかかると思います。
2013年

1.4より前のバージョンのJavaでは動作しません。また、1回の書き込みで十分であることを保証するものはありません。
ローン侯爵14

1

パーティーには少し遅れますが、さまざまなファイルコピー方法を使用してファイルをコピーするのにかかる時間の比較を以下に示します。私はメソッドを10回ループし、平均を取った。IOストリームを使用したファイル転送が最悪の候補のようです。

さまざまな方法を使用したファイル転送の比較

メソッドは次のとおりです。

private static long fileCopyUsingFileStreams(File fileToCopy, File newFile) throws IOException {
    FileInputStream input = new FileInputStream(fileToCopy);
    FileOutputStream output = new FileOutputStream(newFile);
    byte[] buf = new byte[1024];
    int bytesRead;
    long start = System.currentTimeMillis();
    while ((bytesRead = input.read(buf)) > 0)
    {
        output.write(buf, 0, bytesRead);
    }
    long end = System.currentTimeMillis();

    input.close();
    output.close();

    return (end-start);
}

private static long fileCopyUsingNIOChannelClass(File fileToCopy, File newFile) throws IOException
{
    FileInputStream inputStream = new FileInputStream(fileToCopy);
    FileChannel inChannel = inputStream.getChannel();

    FileOutputStream outputStream = new FileOutputStream(newFile);
    FileChannel outChannel = outputStream.getChannel();

    long start = System.currentTimeMillis();
    inChannel.transferTo(0, fileToCopy.length(), outChannel);
    long end = System.currentTimeMillis();

    inputStream.close();
    outputStream.close();

    return (end-start);
}

private static long fileCopyUsingApacheCommons(File fileToCopy, File newFile) throws IOException
{
    long start = System.currentTimeMillis();
    FileUtils.copyFile(fileToCopy, newFile);
    long end = System.currentTimeMillis();
    return (end-start);
}

private static long fileCopyUsingNIOFilesClass(File fileToCopy, File newFile) throws IOException
{
    Path source = Paths.get(fileToCopy.getPath());
    Path destination = Paths.get(newFile.getPath());
    long start = System.currentTimeMillis();
    Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
    long end = System.currentTimeMillis();

    return (end-start);
}

NIOチャネルクラスを使用しているときに目に見える唯一の欠点は、中間ファイルコピーの進行状況を表示する方法がまだ見つからないように見えることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.