microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java
public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStream, final OutputStream outStream,
            long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext,
            final RequestOptions options, StorageRequest<?, ?, Integer> request, StreamMd5AndLength descriptor)
            throws IOException, StorageException {
        if (rewindSourceStream && sourceStream.markSupported()) {
            sourceStream.reset();
            sourceStream.mark(Constants.MAX_MARK_LENGTH);
        }

        if (descriptor == null) {
            descriptor = new StreamMd5AndLength();
            if (calculateMD5) {
                try {
                    descriptor.setDigest(MessageDigest.getInstance("MD5"));
                }
                catch (final NoSuchAlgorithmException e) {
                    // This wont happen, throw fatal.
                    throw Utility.generateNewUnexpectedStorageException(e);
                }
            }
        }
        else {
            descriptor.setMd5(null);
        }

        if (writeLength < 0) {
            writeLength = Long.MAX_VALUE;
        }

        final byte[] retrievedBuff = new byte[Constants.BUFFER_COPY_LENGTH];
        int nextCopy = (int) Math.min(retrievedBuff.length, writeLength);
        int count = sourceStream.read(retrievedBuff, 0, nextCopy);

        while (nextCopy > 0 && count != -1) {

            // if maximum execution time would be exceeded
            if (Utility.validateMaxExecutionTimeout(options.getOperationExpiryTimeInMs())) {
                // throw an exception
                TimeoutException timeoutException = new TimeoutException(SR.MAXIMUM_EXECUTION_TIMEOUT_EXCEPTION);
                throw Utility.initIOException(timeoutException);
            }

            if (outStream != null) {
                outStream.write(retrievedBuff, 0, count);
            }

            if (calculateMD5) {
                descriptor.getDigest().update(retrievedBuff, 0, count);
            }

            descriptor.setLength(descriptor.getLength() + count);
            descriptor.setCurrentOperationByteCount(descriptor.getCurrentOperationByteCount() + count);

            if (request != null) {
                request.setCurrentRequestByteCount(request.getCurrentRequestByteCount() + count);
                request.setCurrentDescriptor(descriptor);
            }

            nextCopy = (int) Math.min(retrievedBuff.length, writeLength - descriptor.getLength());
            count = sourceStream.read(retrievedBuff, 0, nextCopy);
        }

        if (outStream != null) {
            outStream.flush();
        }

        return descriptor;
    }
public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStream, final OutputStream outStream,
            long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext,
            final RequestOptions options, StorageRequest<?, ?, Integer> request, StreamMd5AndLength descriptor)
            throws IOException, StorageException {
        if (rewindSourceStream && sourceStream.markSupported()) {
            sourceStream.reset();
            sourceStream.mark(Constants.MAX_MARK_LENGTH);
        }

        if (descriptor == null) {
            descriptor = new StreamMd5AndLength();
            if (calculateMD5) {
                try {
                    descriptor.setDigest(MessageDigest.getInstance("MD5"));
                }
                catch (final NoSuchAlgorithmException e) {
                    // This wont happen, throw fatal.
                    throw Utility.generateNewUnexpectedStorageException(e);
                }
            }
        }
        else {
            descriptor.setMd5(null);
        }

        if (writeLength < 0) {
            writeLength = Long.MAX_VALUE;
        }

        final byte[] retrievedBuff = new byte[Constants.BUFFER_COPY_LENGTH];
        int nextCopy = (int) Math.min(retrievedBuff.length, writeLength);
        int count = sourceStream.read(retrievedBuff, 0, nextCopy);

        while (nextCopy > 0 && count != -1) {

            // if maximum execution time would be exceeded
            if (Utility.validateMaxExecutionTimeout(options.getOperationExpiryTimeInMs())) {
                // throw an exception
                TimeoutException timeoutException = new TimeoutException(SR.MAXIMUM_EXECUTION_TIMEOUT_EXCEPTION);
                throw Utility.initIOException(timeoutException);
            }

            if (outStream != null) {
                outStream.write(retrievedBuff, 0, count);
            }

            if (calculateMD5) {
                descriptor.getDigest().update(retrievedBuff, 0, count);
            }

            descriptor.setLength(descriptor.getLength() + count);
            descriptor.setCurrentOperationByteCount(descriptor.getCurrentOperationByteCount() + count);

            if (request != null) {
                request.setCurrentRequestByteCount(request.getCurrentRequestByteCount() + count);
                request.setCurrentDescriptor(descriptor);
            }

            nextCopy = (int) Math.min(retrievedBuff.length, writeLength - descriptor.getLength());
            count = sourceStream.read(retrievedBuff, 0, nextCopy);
        }

        if (outStream != null) {
            outStream.flush();
        }

        return descriptor;
    }
Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and
optionally calculates the MD5 hash for the data.

@param sourceStream
An <code>InputStream</code> object that represents the input stream to use as the source.
@param outStream
An <code>OutputStream</code> object that represents the output stream to use as the destination.
@param writeLength
The number of bytes to read from the stream.
@param rewindSourceStream
<code>true</code> if the input stream should be rewound <strong>before</strong> it is read; otherwise,
<code>false</code>
@param calculateMD5
<code>true</code> if an MD5 hash will be calculated; otherwise, <code>false</code>.
@param opContext
An {@link OperationContext} object that represents the context for the current operation. This object
is used to track requests to the storage service, and to provide additional runtime information about
the operation.
@param options
A {@link RequestOptions} object that specifies any additional options for the request. Namely, the
maximum execution time.
@param request
Used by download resume to set currentRequestByteCount on the request. Otherwise, null is always used.
@param descriptor
A {@Link StreamMd5AndLength} object to append to in the case of recovery action or null if this is not called
from a recovery. This value needs to be passed for recovery in case part of the body has already been read,
the recovery will attempt to download the remaining bytes but will do MD5 validation on the originally
requested range size.
@return A {@link StreamMd5AndLength} object that contains the output stream length, and optionally the MD5 hash.

@throws IOException
If an I/O error occurs.
@throws StorageException
If a storage service error occurred.