web-dev-qa-db-ja.com

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

Javaでファイルをコピーする唯一の方法は、ストリームを開き、バッファを宣言し、1つのファイルを読み込み、それをループ処理し、それを別のSteamに書き出すことを含むことにいつも悩まされています。 Webには、この種のソリューションの類似した、まだわずかに異なる実装が散らばっています。

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

414
Peter

ツールキットが上述したように、Apache Commons IOは、具体的には FileUtils です。 copyFile() ;それはあなたのためにすべての重い持ち上げを処理します。

そして後記として、FileUtilsの最近のバージョン(2.0.1リリースのような)はファイルをコピーするためのNIOの使用を追加したことに注意してください。 NIOは、ファイルコピーのパフォーマンスを大幅に向上させることができます 。これは、NIOルーチンが読み取りや処理ではなく直接OS /ファイルシステムへのコピーを延期するためです。 Java層を介してバイトを書き込む。ですから、パフォーマンスを探しているのであれば、FileUtilsの最新バージョンを使用していることを確認する価値があります。

272
delfuego

私はApacheコモンズのようなメガ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に紹介されたかどうかを学ぶのは非常に難しいことです。

276
Josh

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() );
}

かなりおしゃれですね。

179
Scott
  • これらの方法はパフォーマンス設計されています(これらはオペレーティングシステムのネイティブ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 );
87
Glen Best

Java 7ではそれは簡単です...

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

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

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

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();
    }
}
28
Rakshi

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

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

どうやら、これはJava 7で修正されるかもしれません - http://today.Java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis .html 指が交差しました!

24
brad

GoogleのGuavaライブラリには、 コピー方法もあります

 public static void コピーするファイル from、
 ファイル to)
 throw IOException
あるファイルから別のファイルにすべてのバイトをコピーします。

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

パラメーター:from - ソースファイルto - 宛先ファイル

スローします。 IOException - 入出力エラーが発生した場合IllegalArgumentException - from.equals(to)の場合

</dd>
22
Andrew McKinlay

Java 7では標準で利用可能です、path.copyTo: http://openjdk.Java.net/projects/nio/javadoc/Java/nio/file/Path.htmlhttp://Java.Sun.com/docs/books/tutorial/essential/io/copy.html

ファイルコピーのように一般的で単純なものを標準化するのに非常に長い時間がかかるとは思えません。

19
Ryan

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

  1. GetChannelが例外をスローすると、オープンストリームがリークする可能性があります。
  2. 大きなファイルの場合は、OSが処理できる量を超えて一度に転送しようとしている可能性があります。
  3. TransferFromの戻り値を無視しているので、ファイルの一部だけをコピーしている可能性があります。

これがorg.Apache.tools.ant.util.ResourceUtils.copyResourceがとても複雑な理由です。また、transferFromは問題ありませんが、Linux上のJDK 1.4ではtransferToが機能しません( を参照してください。バグID:5056395 ) - Jesse Glick Jan

7
saji

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);
}

グアバ

ファイル#コピー

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

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

7
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();
        }
    }
}
6
user3200607

私のテストによると、バッファ付きの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();
        }    
    }
}

}

3
Tony

速くてAndroidのJavaのすべてのバージョンで動作します。

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();
}
2
user1079877

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

Comparison of file transfer using various methods

方法は次のとおりです。

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チャンネルクラスを使用している間に私が見ることができる唯一の欠点は、私がまだ中間ファイルコピーの進行状況を表示する方法を見つけることができないようだということです。

1
Vinit Shandilya