001    /**
002     * Copyright 2010-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.maven.wagon;
017    
018    import java.io.File;
019    
020    import org.slf4j.Logger;
021    import org.slf4j.LoggerFactory;
022    
023    import com.amazonaws.services.s3.AmazonS3Client;
024    import com.amazonaws.services.s3.model.AmazonS3Exception;
025    import com.amazonaws.services.s3.model.PutObjectRequest;
026    import com.amazonaws.services.s3.transfer.TransferManager;
027    import com.amazonaws.services.s3.transfer.Upload;
028    
029    public class S3Utils {
030            private static final int KILOBYTE = 1024;
031            private static final int MEGABYTE = 1024 * KILOBYTE;
032            private static final int MULTI_PART_UPLOAD_THRESHOLD = 100 * MEGABYTE;
033            private static final Logger log = LoggerFactory.getLogger(S3Utils.class);
034    
035            /**
036             * Upload a single file to Amazon S3. If the file is larger than 100MB a multi-part upload is used. This splits the file into multiple
037             * smaller chunks with each chunk being uploaded in a different thread. Once all the threads have completed the file is reassembled on
038             * Amazon's side as a single file again.
039             */
040            public static final void upload(File file, PutObjectRequest request, AmazonS3Client client, TransferManager manager) {
041                    // Store the file on S3
042                    if (file.length() < MULTI_PART_UPLOAD_THRESHOLD) {
043                            // Use normal upload for small files
044                            client.putObject(request);
045                    } else {
046                            log.debug("Blocking multi-part upload: " + file.getAbsolutePath());
047                            // Use multi-part upload for large files
048                            blockingMultiPartUpload(request, manager);
049                    }
050            }
051    
052            /**
053             * Use this method to reliably upload large files by splitting it up into manageable chunks and using separate threads to upload each
054             * chunk. Amazon recommends using a multi-part upload on files larger than 100MB. When this method returns all of the upload threads
055             * that handle portions of the file have completed. The file has also been reassembled on Amazon S3 and is ready for use.
056             */
057            public static final void blockingMultiPartUpload(PutObjectRequest request, TransferManager manager) {
058                    // Use multi-part upload for large files
059                    Upload upload = manager.upload(request);
060                    try {
061                            // Block and wait for the upload to finish
062                            upload.waitForCompletion();
063                    } catch (Exception e) {
064                            throw new AmazonS3Exception("Unexpected error uploading file", e);
065                    }
066            }
067    
068    }