2014-03-19 34 views
1

我试图找到一种方式,可以最快的方式来复制大文件...以最快的方式复制大文件可能

import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.util.ArrayList; 

public class FastFileCopy { 


public static void main(String[] args) { 
    try { 
     String from = "..."; 
     String to = "..."; 
     FileInputStream fis = new FileInputStream(from); 
     FileOutputStream fos = new FileOutputStream(to); 
     ArrayList<Transfer> transfers = new ArrayList<>(); 
     long position = 0, estimate; 
     int count = 1024 * 64; 
     boolean lastChunk = false; 
     while (true) { 
      if (position + count < fis.getChannel().size()) { 
       transfers.add(new Transfer(fis, fos, position, position + count)); 
       position += count + 1; 
       estimate = position + count; 
       if (estimate >= fis.getChannel().size()) { 
        lastChunk = true; 
       } 
      } else { 
       lastChunk = true; 
      } 
      if (lastChunk) { 
       transfers.add(new Transfer(fis, fos, position, fis.getChannel().size())); 
       break; 
      } 
     } 
     for (Transfer transfer : transfers) { 
      transfer.start(); 
     } 
    } catch (IOException ex) { 
     ex.printStackTrace(); 
    } 
} 

} 

然后创建这个类:

import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.nio.channels.FileChannel; 

public class Transfer extends Thread { 

private FileChannel inChannel = null; 
private FileChannel outChannel = null; 
private long position, count; 

public Transfer(FileInputStream fis, FileOutputStream fos, long position, long count) { 
    this.position = position; 
    this.count = count; 
    inChannel = fis.getChannel(); 
    outChannel = fos.getChannel(); 
} 

@Override 
public void run() { 
    try { 
     inChannel.transferTo(position, count, outChannel); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

} 

我测试了它和结果是非常非常令人印象深刻的... 但有一个大问题,复制的文件比当前文件大veryyyyy!

的话,请检查一下,帮我找到了问题,谢谢:))

+4

'Files.copy(source,destination)'对你来说不够快吗?另外,如果文件位于单个硬盘上,使用多个线程将会降低***性能。 – assylias

+0

否:))...你可以用这种方式在20秒内复制3GB – AvB

+0

你至少有_tried_ Files Files()吗? – fge

回答

2

由于每个循环中,您增加通过计数+ 1的位置,以及你与`(FIS,FOS,位置,位置+计数)传输时,你的代码将创建传输对象如下:

new Transfer(fis, fos, 0,count) 
new Transfer(fis, fos, count+1, 2count+1) 
new Transfer(fis, fos, 2count+2, 3count+2) 
new Transfer(fis, fos, 3count+3, 4count+3) 
... 

因此,虽然您将创建filesize/count传输类,但您要求总共传输(count + 1) * (1 + 2 + 3 + ...)个字节。

此外,我不认为FileChannel.TransferTo()以您认为的方式工作。 position指定开始阅读的源文件中的位置。它没有指定您在目标频道中写入的位置。所以,即使你的尺寸正确,你最终会得到正确尺寸的输出文件,但内容将以线程写入它们的顺序混乱。您可以拨打outChannel.position()跳到正确的位置。我不清楚什么样的混乱可能会发生,因为多个线程以这种方式扩展文件大小。


实验很好,我鼓励你试试这个和基准。然而,评论是正确的,这种方法是错误的。只有一个磁盘,只有一个文件系统缓冲区支持,并有多个线程争夺它不会使其工作更快 - 并可能使其变慢。

你就不可能改善:

long count = 0; 
long size = src.size(); 
while(count < size) { 
    count += src.transferTo(count, size - count, dest); 
} 

也确实注意到,这是非常困难的有关文件操作的性能判断,因为文件系统会缓存读取和写入,这样一个可怕的很多你做的只是在RAM上超便宜的操作。

另请注意,至少在进行基准测试时,在考虑复制完成之前,您将需要join()以及您开始的所有线程。

+0

非常感谢你:) – AvB

5

这是一个XY问题。只需使用Files.copy()

看那个,看看这是不是你不够快:

$ ls -lh ~/ubuntu-13.04-desktop-amd64.iso 
-rw-rw-r-- 1 fge fge 785M Jul 12 2013 /home/fge/ubuntu-13.04-desktop-amd64.iso 
$ cat Foo.java 
import java.io.IOException; 
import java.nio.file.Files; 
import java.nio.file.Paths; 
import java.nio.file.StandardCopyOption; 

public class Foo 
{ 
    public static void main(final String... args) 
     throws IOException 
    { 
     Files.copy(Paths.get("/home/fge/ubuntu-13.04-desktop-amd64.iso"), 
      Paths.get("/tmp/t.iso"), StandardCopyOption.REPLACE_EXISTING); 
    } 
} 
$ time java Foo 

real 0m1.860s 
user 0m0.077s 
sys 0m0.648s 
$ time java Foo 

real 0m1.851s 
user 0m0.101s 
sys 0m0.598s 

它可能是更快。上帝知道为什么,Oracle不使用sendfile(2),即使这是Java 8并且Linux 2.2在这里已经有相当长的一段时间了。

+1

在任何有自尊心的开发人员的机器上,这个程序会将文件缓存在内存中,操作系统将在程序结束后写回脏的FS缓存*。在progream执行后添加时间同步并添加时间。 –

+0

@fge谢谢:) – AvB

相关问题