【问题标题】:Fastest way to copy files in Java用Java复制文件的最快方法
【发布时间】:2010-10-23 15:43:04
【问题描述】:

在 Java 中复制大量文件的最快方法是什么。到目前为止,我已经使用了文件流和 nio。整体流似乎比 nio 快。到目前为止你有什么经验?

【问题讨论】:

  • 您是否在复制文件时对文件进行某种转换?为什么不直接使用操作系统的文件系统功能?
  • 不,我没有进行任何转换。但是当复制超过 10,000 个文件时,错误处理会更加困难,并且在复制小文件时产生系统线程的开销很大。
  • 我建议您修改您的问题以包含我们发现的限制条件。
  • 你想并行做多少?
  • 你试过 tar 管道吗? rsync?(系统管理员可以在这里提供帮助 - 可能用于 ServerFault)

标签: java file-io


【解决方案1】:

使用流

private static void copyFileUsingStream(File source, File dest) throws IOException {
    InputStream is = null;
    OutputStream os = null;
    try {
        is = new FileInputStream(source);
        os = new FileOutputStream(dest);
        byte[] buffer = new byte[1024];
        int length;
        while ((length = is.read(buffer)) > 0) {
            os.write(buffer, 0, length);
        }
    } finally {
        is.close();
        os.close();
    }
}

使用频道

private static void copyFileUsingChannel(File source, File dest) throws IOException {
    FileChannel sourceChannel = null;
    FileChannel destChannel = null;
    try {
        sourceChannel = new FileInputStream(source).getChannel();
        destChannel = new FileOutputStream(dest).getChannel();
        destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
       }finally{
           sourceChannel.close();
           destChannel.close();
       }
}

使用 Apache Commons IO

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

使用 Java SE 7 文件

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

性能测试

File source = new File("/Users/tmp/source.avi");
File dest = new File("/Users/tmp/dest.avi");

//copy file conventional way using Stream
long start = System.nanoTime();
copyFileUsingStream(source, dest);
System.out.println("Time taken by Stream Copy = "+(System.nanoTime()-start));

//copy files using java.nio FileChannel
source = new File("/Users/tmp/sourceChannel.avi");
dest = new File("/Users/tmp/destChannel.avi");
start = System.nanoTime();
copyFileUsingChannel(source, dest);
System.out.println("Time taken by Channel Copy = "+(System.nanoTime()-start));

//copy files using apache commons io
source = new File("/Users/tmp/sourceApache.avi");
dest = new File("/Users/tmp/destApache.avi");
start = System.nanoTime();
copyFileUsingApacheCommonsIO(source, dest);
System.out.println("Time taken by Apache Commons IO Copy = "+(System.nanoTime()-start));

//using Java 7 Files class
source = new File("/Users/tmp/sourceJava7.avi");
dest = new File("/Users/tmp/destJava7.avi");
start = System.nanoTime();
copyFileUsingJava7Files(source, dest);
System.out.println("Time taken by Java7 Files Copy = "+(System.nanoTime()-start));

结果

Time taken by Stream Copy            =  44,582,575,000
Time taken by Java7 Files Copy       =  89,061,578,000
Time taken by Channel Copy           = 104,138,195,000
Time taken by Apache Commons IO Copy = 108,396,714,000

【讨论】:

  • 最好只粘贴此链接journaldev.com/861/java-copy-file :)
  • @Mak 粘贴链接不好,因为链接在未来可能会失效。例如:未接受的答案之一中的 oracle 链接不再有效。
【解决方案2】:

http://www.baptiste-wicht.com/2010/08/file-copy-in-java-benchmark/ 可能会给你答案。

对于基准测试,我使用不同的文件进行了测试。

  1. 小文件 (5 KB)
  2. 中等文件 (50 KB)
  3. 大文件 (5 MB)
  4. 胖文件 (50 MB)
  5. 还有一个巨大的文件(1.3 GB)只有二进制

我首先使用文本文件进行测试,然后使用二进制文件。我使用三种模式进行了测试:

  1. 在同一个硬盘上。它是一个 250 GB 的 IDE 硬盘,带有 8 MB 的缓存。它采用 Ext4 格式。
  2. 两个磁盘之间。我使用了第一个磁盘和另一个 250 GB 的 SATA 硬盘和 16 MB 的缓存。它采用 Ext4 格式。
  3. 两个磁盘之间。我使用了第一个磁盘和另一个具有 32 MB 缓存的 1 TB SATA 硬盘。它使用 NTFS 格式化。

我使用基准框架described here 对所有方法进行测试。测试是在我的个人电脑上进行的(Ubuntu 10.04 64 位、Intel Core 2 Duo 3.16 GHz、6 Go DDR2、SATA 硬盘)。使用的 Java 版本是 Java 7 64 位虚拟机...

【讨论】:

    【解决方案3】:

    我会使用:

    import java.io.*;
    import java.nio.channels.*;
    
    public class FileUtils{
        public static void copyFile(File in, File out) 
            throws IOException 
        {
            FileChannel inChannel = new
                FileInputStream(in).getChannel();
            FileChannel outChannel = new
                FileOutputStream(out).getChannel();
            try {
                inChannel.transferTo(0, inChannel.size(),
                        outChannel);
            } 
            catch (IOException e) {
                throw e;
            }
            finally {
                if (inChannel != null) inChannel.close();
                if (outChannel != null) outChannel.close();
            }
        }
    
        public static void main(String args[]) throws IOException{
            FileUtils.copyFile(new File(args[0]),new File(args[1]));
      }
    }
    

    如果您的任何文件在 Windows 中大于 64M,您可能需要查看以下内容: http://forums.sun.com/thread.jspa?threadID=439695&messageID=2917510

    【讨论】:

    • catch (IOException e) { throw e; }
    • 谢谢,这比我使用的要快得多!
    • 我的 IDE 警告我应该关闭 InputStream 而不是通道...对此有何评论?
    • @basZero stackoverflow.com/a/8210815/313137 说他们都应该工作
    【解决方案4】:

    它依赖于文件(更大的文件),对我来说是缓冲流的最快方式

     public void copyFile(File inFileStr, File outFileStr) throws IOException {
    
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inFileStr)); 
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFileStr))) {
    
            byte[] buffer = new byte[1024 * 1024];
            int read = 0;
            while ((read = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, read);
            }
    
            bis.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      您可以使用 apache commons-io 库的 FileUtils 实现来复制文件

      FileUtils.copyFile(new File(sourcePath), new File(destPath));
      

      其中使用 FileChannel 进行 IO 操作。

      或者使用 java.nio.file.Filescopy() 方法。

      【讨论】:

        【解决方案6】:

        让 java fork 复制文件的操作系统批处理脚本。您的代码可能必须编写批处理脚本。

        【讨论】:

        • 已经考虑过了,但是在复制 10,000 多个文件时错误处理会很困难,并且在复制小文件时产生系统线程的开销很大。此外,该应用程序不会独立于平台。
        • 1.无论如何,您将不得不处理错误检查。 2. 你是对的,它不会独立于平台——所以你打算在不同类型的服务器上运行它? 3. 你能在“适当”的地方创建这 10,000 个文件,而根本不需要副本吗? 4. 不要为每个文件生成一个线程。每 100 个文件一个线程或其他东西。
        • 我同意这一点。文件的愚蠢复制不是 Java 的理想用例。如果您想从 Java 中执行此操作,请分叉操作系统级别的调用。
        猜你喜欢
        • 2021-07-24
        • 2012-07-02
        • 1970-01-01
        • 2018-09-07
        • 1970-01-01
        • 2015-09-13
        • 2010-11-25
        • 1970-01-01
        相关资源
        最近更新 更多