private static void doCopyFile(final File srcFile, final File destFile, final boolean preserveFileDate)
            throws IOException {
        if (destFile.exists() && destFile.isDirectory()) {
            throw new IOException("Destination '" + destFile + "' exists but is a directory");
        }

        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel input = null;
        FileChannel output = null;
        try {
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            input = fis.getChannel();
            output = fos.getChannel();
            final long size = input.size(); // TODO See IO-386
            long pos = 0;
            long count = 0;
            while (pos < size) {
                final long remain = size - pos;
                count = remain > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : remain;
                final long bytesCopied = output.transferFrom(input, pos, count);
                if (bytesCopied == 0) { // IO-385 - can happen if file is truncated after caching the size
                    break; // ensure we don't loop forever
                }
                pos += bytesCopied;
            }
        } finally {
            CloseableUtils.closeQuietly(output, fos, input, fis);
        }

        final long srcLen = srcFile.length(); // TODO See IO-386
        final long dstLen = destFile.length(); // TODO See IO-386
        if (srcLen != dstLen) {
            throw new IOException("Failed to copy full contents from '" +
                    srcFile + "' to '" + destFile + "' Expected length: " + srcLen + " Actual: " + dstLen);
        }
        if (preserveFileDate) {
            destFile.setLastModified(srcFile.lastModified());
        }
    }
Internal copy file method.
This caches the original file length, and throws an IOException
if the output file length is different from the current input file length.
So it may fail if the file changes size.
It may also fail with "IllegalArgumentException: Negative size" if the input file is truncated part way
through copying the data and the new file size is less than the current position.

@param srcFile          the validated source file, must not be {@code null}
@param destFile         the validated destination file, must not be {@code null}
@param preserveFileDate whether to preserve the file date
@throws IOException              if an error occurs
@throws IOException              if the output file length is not the same as the input file length after the
copy completes
@throws IllegalArgumentException "Negative size" if the file is truncated so that the size is less than the
position
replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/utils/FileUtils.java