在Java中使用FileChannel连接大型文件时,哪种方法更有效

在Java中使用FileChannel连接大型文件时,哪种方法更有效,java,concatenation,nio,bytebuffer,filechannel,Java,Concatenation,Nio,Bytebuffer,Filechannel,我想找出哪种方法比我用Java连接文本文件的两种方法更好。如果有人对内核级的情况有一些见解,能够解释这些写入文件通道的方法之间的差异,我将不胜感激 从我从文档和其他堆栈溢出对话中了解到,allocateDirect在驱动器上分配空间,并且主要避免使用RAM。我担心使用allocateDirect创建的ByteBuffer可能会溢出,或者如果文件填充很大(比如1GB),则可能无法分配。在软件开发过程中,我保证文件不会超过2GB;但在未来,它的容量可能会达到10或20GB 我观察到transferF

我想找出哪种方法比我用Java连接文本文件的两种方法更好。如果有人对内核级的情况有一些见解,能够解释这些写入文件通道的方法之间的差异,我将不胜感激

从我从文档和其他堆栈溢出对话中了解到,allocateDirect在驱动器上分配空间,并且主要避免使用RAM。我担心使用allocateDirect创建的ByteBuffer可能会溢出,或者如果文件填充很大(比如1GB),则可能无法分配。在软件开发过程中,我保证文件不会超过2GB;但在未来,它的容量可能会达到10或20GB

我观察到transferFrom循环从来不会多次通过循环。。。因此,它似乎成功地一次编写了整个内嵌;但我还没有用大于60MB的文件测试过它。不过我还是循环了一下,因为文档中规定,无法保证一次会写多少。由于transferFrom只能在我的系统上接受一个int32作为其计数参数,我将无法指定一次传输超过2GB的数据。。。同样,内核专业知识将帮助我理解

提前感谢您的帮助

使用ByteBuffer

boolean concatFiles(StringBuffer sb, File infile, File outfile) {

    FileChannel inChan = null, outChan = null;

    try {

        ByteBuffer buff = ByteBuffer.allocateDirect((int)(infile.length() + sb.length()));
        //write the stringBuffer so it goes in the output file first:
        buff.put(sb.toString().getBytes());

        //create the FileChannels:
        inChan  = new RandomAccessFile(infile,  "r" ).getChannel();
        outChan = new RandomAccessFile(outfile, "rw").getChannel();

        //read the infile in to the buffer:
        inChan.read(buff);

        // prep the buffer:
        buff.flip();

        // write the buffer out to the file via the FileChannel:
        outChan.write(buff);
        inChan.close();
        outChan.close();
     } catch...etc

}
boolean concatFiles(StringBuffer sb, File infile, File outfile) {

    FileChannel inChan = null, outChan = null;

    try {

        //write the stringBuffer so it goes in the output file first:    
        PrintWriter  fw = new PrintWriter(outfile);
        fw.write(sb.toString());
        fw.flush();
        fw.close();

        // create the channels appropriate for appending:
        outChan = new FileOutputStream(outfile, true).getChannel();
        inChan  = new RandomAccessFile(infile, "r").getChannel();

        long startSize = outfile.length();
        long inFileSize = infile.length();
        long bytesWritten = 0;

        //set the position where we should start appending the data:
        outChan.position(startSize);
        Byte startByte = outChan.position();

        while(bytesWritten < length){ 
            bytesWritten += outChan.transferFrom(inChan, startByte, (int) inFileSize);
            startByte = bytesWritten + 1;
        }

        inChan.close();
        outChan.close();
    } catch ... etc
使用Transferto(或transferFrom)

boolean concatFiles(StringBuffer sb, File infile, File outfile) {

    FileChannel inChan = null, outChan = null;

    try {

        ByteBuffer buff = ByteBuffer.allocateDirect((int)(infile.length() + sb.length()));
        //write the stringBuffer so it goes in the output file first:
        buff.put(sb.toString().getBytes());

        //create the FileChannels:
        inChan  = new RandomAccessFile(infile,  "r" ).getChannel();
        outChan = new RandomAccessFile(outfile, "rw").getChannel();

        //read the infile in to the buffer:
        inChan.read(buff);

        // prep the buffer:
        buff.flip();

        // write the buffer out to the file via the FileChannel:
        outChan.write(buff);
        inChan.close();
        outChan.close();
     } catch...etc

}
boolean concatFiles(StringBuffer sb, File infile, File outfile) {

    FileChannel inChan = null, outChan = null;

    try {

        //write the stringBuffer so it goes in the output file first:    
        PrintWriter  fw = new PrintWriter(outfile);
        fw.write(sb.toString());
        fw.flush();
        fw.close();

        // create the channels appropriate for appending:
        outChan = new FileOutputStream(outfile, true).getChannel();
        inChan  = new RandomAccessFile(infile, "r").getChannel();

        long startSize = outfile.length();
        long inFileSize = infile.length();
        long bytesWritten = 0;

        //set the position where we should start appending the data:
        outChan.position(startSize);
        Byte startByte = outChan.position();

        while(bytesWritten < length){ 
            bytesWritten += outChan.transferFrom(inChan, startByte, (int) inFileSize);
            startByte = bytesWritten + 1;
        }

        inChan.close();
        outChan.close();
    } catch ... etc
布尔连接文件(StringBuffer sb、文件填充、文件输出){
FileChannel inChan=null,outChan=null;
试一试{
//写入stringBuffer,使其首先进入输出文件:
PrintWriter fw=新的PrintWriter(输出文件);
fw.写(sb.toString());
fw.flush();
fw.close();
//创建适合附加的通道:
outChan=新文件outputstream(outfile,true).getChannel();
inChan=new RandomAccessFile(infle,“r”).getChannel();
long startSize=outfile.length();
long inFileSize=infie.length();
长字节写入=0;
//设置开始添加数据的位置:
出口位置(起始尺寸);
字节startByte=outChan.position();
而(字节数<长度){
BytesWrited+=outChan.transferFrom(inChan,startByte,(int)infiresize);
startByte=字节写入+1;
}
inChan.close();
outChan.close();
}接住…等
transferTo()可以更高效,因为数据复制更少,或者如果可以在内核中完成,那么就没有数据复制。如果它不在您的平台上,它仍然会使用经过高度调优的代码


您确实需要循环,总有一天它会迭代,您的代码会继续工作。

我明白了……虽然allocateDirect直接在磁盘上分配空间,这对JVM内存管理更有利,但数据仍然会从一个地方复制到另一个地方。另一方面,transferTo/from实际上可以避免完全复制这些字节,让内核决定是否需要将数据从源主动复制到目标,或者是否只需更改有关磁盘上字节位置的信息。是否正确?@GLaDOS allocateDirect()不“直接在磁盘上分配空间”。您将直接缓冲区与映射缓冲区混淆。谢谢;因此,从我在这篇评论后阅读的内容来看,映射缓冲区是一种直接缓冲区,表示文件的内存映射区域。看起来我还有很多要学的。顺便说一句,关于“总有一天它会迭代”,这暴露了我对FileChannel position()的误解。当我第一次阅读文档时,它似乎没有理解:“此方法不会修改此通道的位置。“…也就是说,transferFrom不会修改位置…:-|注意,我只是在while循环中将其更改为使用startByte而不是outChan.position()。我以前的理解是,每次outChan.transferFrom运行时,outChan.position()将返回最新写入的位置。事实证明,当我们对大型文件使用此函数时,transferFrom()参数中的outChan.position()会返回原始位置,而不是更新的位置。因此,我们现在使用单独的变量来保留下一个位置。