Android Open Source - android-simpl3r Uploader






From Project

Back to project page android-simpl3r.

License

The source code is released under:

Apache License

If you think the Android project android-simpl3r listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/***
 * Copyright (c) 2012 readyState Software Ltd
 * //from w w  w  . j  a v  a2  s . c o  m
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.readystatesoftware.simpl3r;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest;
import com.amazonaws.services.s3.model.CompleteMultipartUploadResult;
import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
import com.amazonaws.services.s3.model.PartETag;
import com.amazonaws.services.s3.model.ProgressEvent;
import com.amazonaws.services.s3.model.ProgressListener;
import com.amazonaws.services.s3.model.UploadPartRequest;
import com.amazonaws.services.s3.model.UploadPartResult;
import com.readystatesoftware.simpl3r.utils.SharedPreferencesCompat;
import com.readystatesoftware.simpl3r.utils.SharedPreferencesUtils;

public class Uploader {
  
  private static final long MIN_DEFAULT_PART_SIZE = 5 * 1024 * 1024;
  
  private static final String TAG = "Simpl3r";
  private static final String PREFS_NAME = "preferences_simpl3r";
  private static final String PREFS_UPLOAD_ID = "_uploadId";
  private static final String PREFS_ETAGS = "_etags";
  private static final String PREFS_ETAG_SEP = "~~";
  
  private AmazonS3Client s3Client;
  private String s3bucketName;
  private String s3key;
  private File file;
  
  private SharedPreferences prefs;
  private long partSize = MIN_DEFAULT_PART_SIZE;  
  private UploadProgressListener progressListener;
  private long bytesUploaded = 0;
  private boolean userInterrupted = false;
  private boolean userAborted = false;
  
  public Uploader(Context context, AmazonS3Client s3Client, String s3bucketName, String s3key, File file) {
    this.s3Client = s3Client;
    this.s3key = s3key;
    this.s3bucketName = s3bucketName;
    this.file = file;
    prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
  }
  
  /**
   * Initiate a multipart file upload to Amazon S3
   * 
   * @return the URL of a successfully uploaded file
   */
  public String start() {
    
    // initialize
    List<PartETag> partETags = new ArrayList<PartETag>();
    final long contentLength = file.length();
    long filePosition = 0;
    int startPartNumber = 1;
    
    userInterrupted = false;
    userAborted = false;
    bytesUploaded = 0;
    
    // check if we can resume an incomplete download
    String uploadId = getCachedUploadId();
    
    if (uploadId != null) {
      // we can resume the download
      Log.i(TAG, "resuming upload for " + uploadId);
      
      // get the cached etags
      List<PartETag> cachedEtags = getCachedPartEtags();
      partETags.addAll(cachedEtags);
            
      // calculate the start position for resume
      startPartNumber = cachedEtags.size() + 1;
      filePosition = (startPartNumber -1) * partSize;
      bytesUploaded = filePosition;
      
      Log.i(TAG, "resuming at part " + startPartNumber + " position " + filePosition);
    
    } else {
      // initiate a new multi part upload
      Log.i(TAG, "initiating new upload");
      
          InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(s3bucketName, s3key);
          configureInitiateRequest(initRequest);
          InitiateMultipartUploadResult initResponse = s3Client.initiateMultipartUpload(initRequest);
          uploadId = initResponse.getUploadId();
      
    }
    
    final AbortMultipartUploadRequest abortRequest = new AbortMultipartUploadRequest(s3bucketName, s3key, uploadId);
        
        for (int k = startPartNumber; filePosition < contentLength; k++) {
          
            long thisPartSize = Math.min(partSize, (contentLength - filePosition));
            
            Log.i(TAG, "starting file part " + k + " with size " + thisPartSize);
            
            UploadPartRequest uploadRequest = new UploadPartRequest().withBucketName(s3bucketName)
                    .withKey(s3key).withUploadId(uploadId)
                    .withPartNumber(k).withFileOffset(filePosition).withFile(file)
                    .withPartSize(thisPartSize);

            ProgressListener s3progressListener = new ProgressListener() {
                public void progressChanged(ProgressEvent progressEvent) {
                    
                  // bail out if user cancelled
                  // TODO calling shutdown too brute force?
                    if (userInterrupted) {
                    s3Client.shutdown(); 
                    throw new UploadIterruptedException("User interrupted");
                  } else if (userAborted) {
                    // aborted requests cannot be resumed, so clear any cached etags
                    clearProgressCache();
                      s3Client.abortMultipartUpload(abortRequest);
                      s3Client.shutdown();
                    }
                    
                    bytesUploaded += progressEvent.getBytesTransfered();
                    
                    //Log.d(TAG, "bytesUploaded=" + bytesUploaded);
                    
                    // broadcast progress
                    float fpercent = ((bytesUploaded * 100) / contentLength);
                    int percent = Math.round(fpercent);
                    if (progressListener != null) {
                      progressListener.progressChanged(progressEvent, bytesUploaded, percent);
                    }
                    
                }
            };
            
            uploadRequest.setProgressListener(s3progressListener);
            
            UploadPartResult result = s3Client.uploadPart(uploadRequest);
            
            partETags.add(result.getPartETag());
            
            // cache the part progress for this upload
            if (k == 1) {
              initProgressCache(uploadId);
            }
            // store part etag
            cachePartEtag(result);
            
            filePosition += thisPartSize;
        }
        
        CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(
            s3bucketName, s3key, uploadId,
                partETags);

        CompleteMultipartUploadResult result = s3Client.completeMultipartUpload(compRequest);
        bytesUploaded = 0;
        
        Log.i(TAG, "upload complete for " + uploadId);
        
        clearProgressCache();
 
        return result.getLocation();
    
  }

  private String getCachedUploadId() {
    return prefs.getString(s3key + PREFS_UPLOAD_ID, null);
  }
  
  private List<PartETag> getCachedPartEtags() {
    List<PartETag> result = new ArrayList<PartETag>();    
    // get the cached etags
    ArrayList<String> etags = SharedPreferencesUtils.getStringArrayPref(prefs, s3key + PREFS_ETAGS);
    for (String etagString : etags) {
      String partNum = etagString.substring(0, etagString.indexOf(PREFS_ETAG_SEP));
      String partTag = etagString.substring(etagString.indexOf(PREFS_ETAG_SEP) + 2, etagString.length());
            
      PartETag etag = new PartETag(Integer.parseInt(partNum), partTag);
      result.add(etag);
    }
    return result;
  }

  private void cachePartEtag(UploadPartResult result) {
    String serialEtag = result.getPartETag().getPartNumber() + PREFS_ETAG_SEP + result.getPartETag().getETag();
    ArrayList<String> etags = SharedPreferencesUtils.getStringArrayPref(prefs, s3key + PREFS_ETAGS);
    etags.add(serialEtag);
    SharedPreferencesUtils.setStringArrayPref(prefs, s3key + PREFS_ETAGS, etags);
  }

  private void initProgressCache(String uploadId) {
    // store uploadID
    Editor edit = prefs.edit().putString(s3key + PREFS_UPLOAD_ID, uploadId);
    SharedPreferencesCompat.apply(edit);
    // create empty etag array
    ArrayList<String> etags = new ArrayList<String>();
    SharedPreferencesUtils.setStringArrayPref(prefs, s3key + PREFS_ETAGS, etags);
  }

  private void clearProgressCache() {
    // clear the cached uploadId and etags
        Editor edit = prefs.edit();
        edit.remove(s3key + PREFS_UPLOAD_ID);
        edit.remove(s3key + PREFS_ETAGS);
      SharedPreferencesCompat.apply(edit);
  }
  
  public void interrupt() {
    userInterrupted = true;
  }
  
  public void abort() {
    userAborted = true;
  }
  
  /**
   * Override to configure the multipart upload request. 
   * 
   * By default uploaded files are publicly readable.
   * 
   * @param initRequest S3 request object for the file to be uploaded
   */
  protected void configureInitiateRequest(InitiateMultipartUploadRequest initRequest) {
    initRequest.setCannedACL(CannedAccessControlList.PublicRead);
  }
  
  public void setPrefs(SharedPreferences prefs) {
    this.prefs = prefs;
  }
  
  public long getPartSize() {
    return partSize;
  }

  public void setPartSize(long partSize) {
    if (partSize < MIN_DEFAULT_PART_SIZE) {
      throw new IllegalStateException("Part size is less than S3 minimum of " + MIN_DEFAULT_PART_SIZE);
    } else {
      this.partSize = partSize;
    }  
  }
  
  public void setProgressListener(UploadProgressListener progressListener) {
    this.progressListener = progressListener;
  }

  public interface UploadProgressListener {
    public void progressChanged(ProgressEvent progressEvent, long bytesUploaded, int percentUploaded);
  }
  
}




Java Source Code List

com.readystatesoftware.simpl3r.UploadIterruptedException.java
com.readystatesoftware.simpl3r.Uploader.java
com.readystatesoftware.simpl3r.example.MainActivity.java
com.readystatesoftware.simpl3r.example.UploadService.java
com.readystatesoftware.simpl3r.utils.SharedPreferencesCompat.java
com.readystatesoftware.simpl3r.utils.SharedPreferencesUtils.java