用Java复制文件的标准简洁方法?

Pet*_*ter 417 java copy file

一直困扰我的是,用Java复制文件的唯一方法是打开流,声明一个缓冲区,读取一个文件,循环遍历它,然后将其写入另一个文件.Web上充斥着类似但仍然略有不同的此类解决方案的实现.

有没有更好的方法保持在Java语言的范围内(意味着不涉及执行特定于操作系统的命令)?也许在一些可靠的开源实用程序包中,这至少会掩盖这个底层实现并提供单行解决方案?

Jos*_*osh 277

我会避免使用像apache commons这样的mega 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)

其中一篇链接文章显示了如何使用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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

学习NIO可能有点棘手,所以你可能想要在离开并试图一夜之间学习NIO之前就相信这个机制.根据个人经验,如果您没有经验并且通过java.io流引入IO,那么学习起来可能是一件非常困难的事情.

  • 上述代码存在三个可能的问题:(a)如果getChannel抛出异常,则可能泄漏开放流; (b)对于大型文件,您可能试图一次性转移比操作系统可以处理的更多; (c)您忽略transferFrom的返回值,因此它可能只是复制文件的一部分.这就是org.apache.tools.ant.util.ResourceUtils.copyResource如此复杂的原因.另请注意,虽然transferFrom没问题,但是在Linux上JDK 1.4上的transferTo会中断:http://bugs.sun.com/bugdatabase/view_bug.do?video_id = 5056395 (15认同)
  • 此代码有*major*问题.必须在循环中调用transferTo().它不保证转移所要求的全部金额. (11认同)
  • 我相信这个更新版本解决了这些问题:https://gist.github.com/889747 (7认同)
  • 谢谢,有用的信息.我仍然会争论像Apache Commons这样的东西,特别是如果它在下面使用nio(正确)的话; 但我同意理解潜在的基本面很重要. (2认同)

del*_*ego 273

正如上面提到的工具包,Apache Commons IO是可行的方法,特别是FileUtils.copyFile() ; 它为您处理所有繁重的工作.

并且作为附言,请注意最新版本的FileUtils(例如2.0.1版本)已经添加了使用NIO来复制文件; NIO可以显着提高文件复制性能,这在很大程度上是因为NIO例程将复制直接推迟到OS /文件系统而不是通过Java层读取和写入字节来处理它.因此,如果您正在寻找性能,可能需要检查您使用的是最新版本的FileUtils.

  • 如果使用Java 7或更高版本,您可以按照@GlenBest的建议使用Files.copy:http://www.stackoverflow.com/a/16600787/44737 (18认同)
  • 截至2010年12月,Apache Commons IO为2.0.1,具有NIO功能.答案已更新. (14认同)
  • 对Android用户的警告:标准Android API中不包含此信息 (4认同)
  • Apache Commons IO的公开发布仍然是1.4,grrrrrrr (2认同)

Sco*_*ott 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() );
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,更好的是,这也可以使用Java 7中引入的新Files类来完成:

public static void copyFile( File from, File to ) throws IOException {
    Files.copy( from.toPath(), to.toPath() );
}
Run Code Online (Sandbox Code Playgroud)

很时髦,是吗?

  • 令人惊讶的是Java在今天之前没有添加这样的东西.某些操作只是编写计算机软件的绝对必要条件.Java的Oracle开发人员可以从操作系统中学习一两件事,看看他们提供的服务,使新手迁移更容易. (15认同)
  • 此代码有*major*问题.必须在循环中调用transferTo().它不保证转移所要求的全部金额. (5认同)
  • 啊,谢谢!我不知道新的"Files"类及其所有辅助函数.它正是我需要的.谢谢你的例子. (2认同)

Gle*_*est 88

  • 这些方法是性能设计的(它们与操作系统本机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;
    }
}
Run Code Online (Sandbox Code Playgroud)

复制目录或文件

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);
Run Code Online (Sandbox Code Playgroud)

移动目录或文件

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);
Run Code Online (Sandbox Code Playgroud)

以递归方式复制目录或文件

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 );
Run Code Online (Sandbox Code Playgroud)


Kev*_*ler 41

在Java 7中很容易......

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

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Run Code Online (Sandbox Code Playgroud)

  • 它简洁,少即是多.他们的答案很好而且很详细,但我在翻阅时却错过了.不幸的是,有很多答案,其中很多都是漫长的,过时的和复杂的,斯科特和格伦的好答案在那里迷失了(我会放弃帮助).我想知道是否可以通过删除exists()和错误消息将其减少到三行来改进我的答案. (11认同)
  • @momo的问题是如何复制文件。 (2认同)
  • 当您需要“路径”时,无需走“文件”的弯路。`Files.copy(Paths.get("original.txt"), Paths.get("copy.txt"), …)` (2认同)

Rak*_*shi 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 它适用于Java 6,没有额外的jar. (15认同)
  • @Rup它比其他答案要好得多,(a)*因为*它有效,(b)因为它不依赖于第三方软件. (2认同)

小智 24

请注意,所有这些机制仅复制文件的内容,而不复制权限等元数据.因此,如果您要在Linux上复制或移动可执行文件.sh文件,则新文件将无法执行.

为了真正复制或移动文件,即获得与从命令行复制相同的结果,您实际上需要使用本机工具.shell脚本或JNI.

显然,这可以在java 7中修复 - http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html.手指交叉!


And*_*lay 22

Google的Guava库也有一个复制方法:

__PRE__
将所有字节从一个文件复制到另一个文件.

警告:如果to表示现有文件,则该文件将被内容覆盖from.如果to并且 from引用同一文件,则将删除该文件的内容.

参数:from.equals(to) - 源文件to- 目标文件

抛出: from - 如果发生I/O错误 to- 如果from


小智 19

在Java 7中标准提供,path.copyTo:http : //openjdk.java.net/projects/nio/javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/教程/本质/ IO/copy.html

我无法相信它们花了这么长时间来标准化文件复制这么常见和简单的东西:(

  • 没有Path.copyTo; 它是Files.copy. (10认同)

小智 7

上述代码存在三个可能的问题:

  1. 如果getChannel抛出异常,则可能会泄漏打开的流.
  2. 对于大型文件,您可能尝试一次性传输比操作系统可以处理的更多文件.
  3. 您忽略了transferFrom的返回值,因此它可能只是复制文件的一部分.

这就是为什么org.apache.tools.ant.util.ResourceUtils.copyResource这么复杂的原因.另请注意,虽然transferFrom正常,但是在Linux上的JDK 1.4上,transferTo会中断(参见错误ID:5056395) - Jesse Glick Jan


Bal*_*jan 7

如果您在已经使用Spring的Web应用程序中,并且如果您不想将Apache Commons IO包含在简单文件复制中,则可以使用Spring框架的FileCopyUtils.


Jas*_*key 7

您可以通过以下三种方式轻松地使用单行代码复制文件!

Java7:

java.nio.file.Files#副本

private static void copyFileUsingJava7Files(File source, File dest) throws IOException {
    Files.copy(source.toPath(), dest.toPath());
}
Run Code Online (Sandbox Code Playgroud)

Appache Commons IO:

文件实用程序#的CopyFile

private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
    FileUtils.copyFile(source, dest);
}
Run Code Online (Sandbox Code Playgroud)

番石榴:

文件复制#

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}
Run Code Online (Sandbox Code Playgroud)


小智 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)