Есть код клиента и сервера на java. нужно улучшить код так, чтобы можно было передавать большие файлы, без изменения буфера
package clientUtils;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;


public class ConnectionManager {
    String ip;
    int port;
    SocketChannel socketChannel;

    public ConnectionManager(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    void makeRequest(File file, FileInputStream fis) throws IOException {
        sendFile(file, fis);
        receiveConfirmation();
    }

    void connect() throws IOException {
        System.out.println("Connected to the server");
        socketChannel = SocketChannel.open(new InetSocketAddress(ip, port));
        socketChannel.configureBlocking(true);
    }

    void closeConnection() throws IOException {
        socketChannel.close();
        System.out.println("Connection closed");
    }
    private void sendFile(File file, FileInputStream fis) throws IOException {

        // Создаем байтовый массив для передачи имени файла и его длины
        byte[] nameBytes = file.getName().getBytes(StandardCharsets.UTF_8);
        ByteBuffer nameLengthBuffer = ByteBuffer.allocate(Integer.BYTES).putInt(nameBytes.length);
        nameLengthBuffer.flip();

        // Отправляем длину имени файла и само имя
        socketChannel.write(nameLengthBuffer);

        ByteBuffer nameBuffer = ByteBuffer.wrap(nameBytes);
        socketChannel.write(nameBuffer);

        // Отправляем длину файла
        int fileLength = (int) file.length();
        ByteBuffer fileLengthBuffer = ByteBuffer.allocate(Integer.BYTES).putInt(fileLength);
        fileLengthBuffer.flip();
        socketChannel.write(fileLengthBuffer);

        // Определяем размер куска
        int chunkSize = 1024;
        byte[] buffer = new byte[chunkSize];

        // Читаем данные из файла по кускам и отправляем их
        int totalBytesRead = 0;
        while (totalBytesRead < fileLength) {
            System.out.println(totalBytesRead + " of " + fileLength);
            int bytesRead = fis.read(buffer, 0, Math.min(chunkSize, fileLength - totalBytesRead));
            if (bytesRead > 0) { // если прочитали что-то из файла
                ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, bytesRead);
                socketChannel.write(byteBuffer); // отправляем текущий кусок
                totalBytesRead += bytesRead; // увеличиваем количество переданных байт
            }
        }
        socketChannel.write(ByteBuffer.allocate(Long.BYTES)); // разделитель

        fis.close();

        System.out.println("File " + file.getName() + " sent successfully");
    }

    private void receiveConfirmation() throws IOException {
        // Читаем длину сообщения
        ByteBuffer lengthBuffer = ByteBuffer.allocate(Integer.BYTES);
        socketChannel.read(lengthBuffer);
        lengthBuffer.flip();
        int messageLength = lengthBuffer.getInt();

        // Читаем само сообщение
        ByteBuffer messageBuffer = ByteBuffer.allocate(messageLength);
        int bytesRead = 0;
        while (bytesRead < messageLength) {
            bytesRead += socketChannel.read(messageBuffer);
        }
        messageBuffer.rewind();
        byte[] messageBytes = new byte[messageBuffer.remaining()];
        messageBuffer.get(messageBytes);
        String message = new String(messageBytes, StandardCharsets.UTF_8);

        System.out.println("Received file: " + message);
    }
}
package serverUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;

public class ConnectionManager {
    private static final String ACK_MESSAGE_PREFIX = "<%s>Ack";
    static final int BUFFER_SIZE = 1024 * 1024;

    ServerSocketChannel serverSocketChannel;
    int port;

    public ConnectionManager(int port) {
        this.port = port;
    }

    public void initialize() throws IOException {
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        System.out.println("Server started on port " + port);
                System.out.println("============ * ============");
    }

    public void registerSelector(Selector selector) throws ClosedChannelException {
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void processQuery(Selector selector, SelectionKey key) throws IOException {
        if (!key.isValid()) {
            return;
        }
        if (key.isAcceptable()) {
            acceptConnection(key, selector);
        } else if (key.isReadable()) {
            handleRead(key);
        }
    }

    private void acceptConnection(SelectionKey key, Selector selector) throws IOException {
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
        SocketChannel clientSocketChannel = serverSocketChannel.accept();
        clientSocketChannel.configureBlocking(false);
        // Attach new ConnectionContext instance to the selection key
        ConnectionContext context = new ConnectionContext();
        clientSocketChannel.register(selector, SelectionKey.OP_READ, context);

        System.out.println("Accepted connection from " + clientSocketChannel.getRemoteAddress());
    }

    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel clientSocketChannel = (SocketChannel) key.channel();
        ConnectionContext connectionContext = (ConnectionContext) key.attachment();

        if (connectionContext == null) {
            connectionContext = new ConnectionContext();
            key.attach(connectionContext);
        }

        ByteBuffer buffer = connectionContext.buffer;
        int bytesRead = clientSocketChannel.read(buffer);

        if (bytesRead < 0) {
            System.out.println("Closed connection with " + clientSocketChannel.getRemoteAddress());
                    clientSocketChannel.close();
            key.cancel();
            System.out.println("============ * ============");
            return;
        }

        buffer.flip();

        // handle file transmission
        if (connectionContext.fileStarted) {
            // check if we need more data to continue reading the file
            if (connectionContext.fileName != null && connectionContext.fileLength >= 0 &&
                    buffer.remaining() >= connectionContext.fileLength + Long.BYTES) {

                // read the file contents
                byte[] fileContent = new byte[connectionContext.fileLength];
                buffer.get(fileContent);

                // check the separator
                connectionContext.separator = buffer.getLong();
                if (connectionContext.separator != 0L) {
                    throw new RuntimeException("Invalid separator");
                }

                // save the file and send an acknowledgement
                saveFile(connectionContext.fileName, fileContent);
                sendAck(clientSocketChannel, connectionContext.fileName);

                // reset context for next file transfer
                connectionContext.fileStarted = false;
                connectionContext.fileCompleted = true;
                connectionContext.fileName = null;
                connectionContext.fileLength = -1;

                // handle any remaining data in buffer
                if (buffer.remaining() > 0) {
                    handleRead(key);
                    return;
                }
            }
        } else {
            // read the header of the file transfer
            while (buffer.hasRemaining() && !connectionContext.fileStarted) {
                // read the name length
                if (connectionContext.fileName == null && buffer.remaining() >= Integer.BYTES) {
                    int nameLength = buffer.getInt();
                    byte[] nameBytes = new byte[nameLength];
                    buffer.get(nameBytes);
                    connectionContext.fileName = new String(nameBytes, StandardCharsets.UTF_8);
                }

                // read the file length
                if (connectionContext.fileLength < 0 && buffer.remaining() >= Integer.BYTES) {
                    connectionContext.fileLength = buffer.getInt();
                    if (connectionContext.fileLength < 0) {
                        throw new RuntimeException("Invalid file length");
                    }
                    connectionContext.fileStarted = true;

                    // check if we need more data to continue reading the file
                    if (buffer.remaining() < connectionContext.fileLength + Long.BYTES) {
                        buffer.compact();
                        return;
                    }
                }
            }
        }

        // handle file transmission continued with remaining data in buffer
        if (connectionContext.fileStarted && !connectionContext.fileCompleted &&
                buffer.remaining() >= connectionContext.fileLength + Long.BYTES) {

            // read the file contents
            byte[] fileContent = new byte[connectionContext.fileLength];
            buffer.get(fileContent);

            // check the separator
            connectionContext.separator = buffer.getLong();
            if (connectionContext.separator != 0L) {
                throw new RuntimeException("Invalid separator");
            }

            // save the file and send an acknowledgement
            saveFile(connectionContext.fileName, fileContent);
            sendAck(clientSocketChannel, connectionContext.fileName);

            // reset context for next file transfer
            connectionContext.fileStarted = false;
            connectionContext.fileCompleted = true;
            connectionContext.fileName = null;
            connectionContext.fileLength = -1;

            // handle any remaining data in buffer
            if (buffer.remaining() > 0) {
                handleRead(key);
                return;
            }
        }

        buffer.compact();
    }

    private void saveFile(String filename, byte[] fileContent) throws IOException {
        File file = new File("files\\" + filename);

        System.out.println("Saving file " + filename);

        try (FileOutputStream outputStream = new FileOutputStream(file, true)) {
            int offset = 0;
            while (offset < fileContent.length) {
                int bytesToWrite = Math.min(BUFFER_SIZE, fileContent.length - offset);
                outputStream.write(fileContent, offset, bytesToWrite);
                outputStream.flush();
                offset += bytesToWrite;
            }
        }
        System.out.println("File " + filename + " saved successfully");
    }

    private void sendAck(SocketChannel clientSocketChannel, String filename) throws IOException {
        String ackMessage = String.format(ACK_MESSAGE_PREFIX, filename);
        byte[] ackMessageBytes = ackMessage.getBytes();
        ByteBuffer buffer = ByteBuffer.allocate(ackMessageBytes.length + Integer.BYTES);
        buffer.putInt(ackMessageBytes.length);
        buffer.put(ackMessageBytes);
        buffer.flip();

        while (buffer.hasRemaining()) {
            clientSocketChannel.write(buffer);
        }

        System.out.println("Sent acknowledgement for " + filename);
        buffer.clear(); // Очищаем буфер перед чтением новых данных
    }
}