我试图找到一种以最快的方式复制大文件的方法......
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();
}
}
}
我测试了它,结果非常令人印象深刻...... 但是有一个很大的问题,复制的文件比当前文件大得多!!!
所以,请检查并帮我找到问题,谢谢:))
答案 0 :(得分: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
它可能会更快。上帝知道为什么,甲骨文并没有使用sendfile(2)
,即使这是Java 8,Linux 2.2已经存在了很长时间。
答案 1 :(得分:2)
由于每个循环都会按位数增加位置+1,并且您使用`(fis,fos,position,position + count)进行传输,您的代码将创建Transfer对象,如下所示:
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上的超级廉价操作。 / p>
另请注意,至少在进行基准测试时,在考虑复制完成之前,您需要join()
使用已启动的所有线程。