private Upload doUpload(final PutObjectRequest putObjectRequest,
            final TransferStateChangeListener stateListener,
            final S3ProgressListener progressListener,
            final PersistableUpload persistableUpload) throws AmazonServiceException,
            AmazonClientException {

        assertNotObjectLambdaArn(putObjectRequest.getBucketName(), "upload");

        appendSingleObjectUserAgent(putObjectRequest);

        String multipartUploadId = persistableUpload != null ? persistableUpload
                .getMultipartUploadId() : null;

        if (putObjectRequest.getMetadata() == null)
            putObjectRequest.setMetadata(new ObjectMetadata());
        ObjectMetadata metadata = putObjectRequest.getMetadata();

        File file = TransferManagerUtils.getRequestFile(putObjectRequest);

        if ( file != null ) {
            // Always set the content length, even if it's already set
            metadata.setContentLength(file.length());

            // Only set the content type if it hasn't already been set
            if ( metadata.getContentType() == null ) {
                metadata.setContentType(Mimetypes.getInstance().getMimetype(file));
            }
        } else {
            if (multipartUploadId != null) {
                throw new IllegalArgumentException(
                        "Unable to resume the upload. No file specified.");
            }
        }

        String description = "Uploading to " + putObjectRequest.getBucketName()
                + "/" + putObjectRequest.getKey();
        TransferProgress transferProgress = new TransferProgress();
        transferProgress.setTotalBytesToTransfer(TransferManagerUtils
                .getContentLength(putObjectRequest));

        S3ProgressListenerChain listenerChain = new S3ProgressListenerChain(
                new TransferProgressUpdatingListener(transferProgress),
                putObjectRequest.getGeneralProgressListener(), progressListener);

        putObjectRequest.setGeneralProgressListener(listenerChain);

        UploadImpl upload = new UploadImpl(description, transferProgress,
                listenerChain, stateListener);
        /**
         * Since we use the same thread pool for uploading individual parts and
         * complete multi part upload, there is a possibility that the tasks for
         * complete multi-part upload will be added to end of queue in case of
         * multiple parallel uploads submitted. This may result in a delay for
         * processing the complete multi part upload request.
         */
        UploadCallable uploadCallable = new UploadCallable(this, executorService,
                                                           upload, putObjectRequest, listenerChain, multipartUploadId,
                                                           transferProgress);
        UploadMonitor watcher = UploadMonitor.create(this, upload, executorService,
                                                     uploadCallable, putObjectRequest, listenerChain);
        upload.setMonitor(watcher);

        return upload;
    }
<p>
Schedules a new transfer to upload data to Amazon S3. This method is
non-blocking and returns immediately (i.e. before the upload has
finished).
</p>
<p>
Use the returned <code>Upload</code> object to query the progress of the
transfer, add listeners for progress events, and wait for the upload to
complete.
</p>
<p>
If resources are available, the upload will begin immediately. Otherwise,
the upload is scheduled and started as soon as resources become
available.
</p>
<p>
If you are uploading <a href="http://aws.amazon.com/kms/">Amazon Web Services
KMS</a>-encrypted objects, you need to specify the correct region of the
bucket on your client and configure Amazon Web Services Signature Version 4 for added
security. For more information on how to do this, see
http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#
specify-signature-version
</p>

@param putObjectRequest
           The request containing all the parameters for the upload.
@param stateListener
           The transfer state change listener to monitor the upload.
@param progressListener
           An optional callback listener to receive the progress of the
           upload.

@return A new <code>Upload</code> object to use to check the state of the
        upload, listen for progress notifications, and otherwise manage
        the upload.

@throws AmazonClientException
            If any errors are encountered in the client while making the
            request or handling the response.
@throws AmazonServiceException
            If any errors occurred in Amazon S3 while processing the
            request.
aws-java-sdk-s3/src/main/java/com/amazonaws/services/s3/transfer/TransferManager.java