Какой метод более эффективен для объединения больших файлов в Java с помощью FileChannels

Я хочу выяснить, какой из двух методов лучше всего подходит для объединения моих текстовых файлов на Java. Если у кого-то есть понимание того, что он может рассказать о том, что происходит на уровне ядра, которое объясняет разницу между этими методами записи в FileChannel, я был бы очень признателен.

Из того, что я понимаю из документации и других разговоров о переполнении стека, allocateDirect выделяет пространство прямо на диске и в основном избегает использования оперативной памяти. У меня есть опасение, что ByteBuffer, созданный с помощью allocateDirect, может потенциально переполниться или не быть выделенным, если файл имеет большой размер, например, 1 ГБ. На этом этапе разработки нашего программного обеспечения я гарантирую, что размер файла не будет превышать 2 ГБ; но в будущем есть вероятность, что он может достигать 10 или 20 ГБ.

Я заметил, что цикл TransferFrom никогда не проходит цикл более одного раза... так что, похоже, он успешно записывает весь infile одновременно; но я не проверял его с файлами размером более 60 МБ. Я сделал петлю, хотя, потому что документация указывает, что нет никакой гарантии того, сколько будет написано сразу. Поскольку TransferFrom может принимать в моей системе только параметр int32 в качестве параметра подсчета, я не смогу указать, что за один раз будет передаваться более 2 ГБ... Опять же, опыт работы с ядром помог бы мне понять.

Заранее спасибо за помощь!!

Использование 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

}

Использование trasferTo (или TransferFrom):

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

1 ответ

Решение

TransferTo() может быть гораздо более эффективным, так как копирование данных происходит меньше, или вообще без него, если все это можно сделать в ядре. И если он не на вашей платформе, он все равно будет использовать сильно настроенный код.

Вам нужен цикл, однажды он будет повторяться и ваш код продолжит работать.

Другие вопросы по тегам