public static long copyLarge(InputStream input, OutputStream output,
                                 final long inputOffset, final long length, byte[] buffer) throws IOException {
        if (inputOffset > 0) {
            skipFully(input, inputOffset);
        }
        if (length == 0) {
            return 0;
        }
        final int bufferLength = buffer.length;
        int bytesToRead = bufferLength;
        if (length > 0 && length < bufferLength) {
            bytesToRead = (int) length;
        }
        int read;
        long totalRead = 0;
        while (bytesToRead > 0 && EOF != (read = input.read(buffer, 0, bytesToRead))) {
            output.write(buffer, 0, read);
            totalRead += read;
            if (length > 0) { // only adjust length if not reading to the end
                // Note the cast must work because buffer.length is an integer
                bytesToRead = (int) Math.min(length - totalRead, bufferLength);
            }
        }
        return totalRead;
    }
Copy some or all bytes from a large (over 2GB) <code>InputStream</code> to an
<code>OutputStream</code>, optionally skipping input bytes.
<p>
This method uses the provided buffer, so there is no need to use a
<code>BufferedInputStream</code>.
<p>

@param input       the <code>InputStream</code> to read from
@param output      the <code>OutputStream</code> to write to
@param inputOffset : number of bytes to skip from input before copying
                   -ve values are ignored
@param length      : number of bytes to copy. -ve means all
@param buffer      the buffer to use for the copy
@return the number of bytes copied
@throws NullPointerException if the input or output is null
@throws IOException          if an I/O error occurs
@since 2.2
pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java