Search code examples
androidkotlinamazon-s3awss3transferutilityaws-s3-client

How do I do S3 multipart upload from Android


Overview: I have an app that uploads images and small audio files to S3 using the following approach:

  1. Get a presigned URL from the backend using a secure API.
  2. Upload to the presigned URL using Retrofit.

Problem: Now I need to support larger files like videos, so I am looking into implementing multi-part uploads.

Previous Solution: In a web app I did a while back, I had used the Uppy S3 Multipart plugin to handle multi-part uploads. The plugin had callbacks through which I could provide it with signed URLs for the individual parts. Everything else (splitting into parts, uploading) was handled by the plugin.

Current Situation: I have been searching for days for a viable alternative to Uppy S3 Multipart in Android but have not found one. Here are my findings:

  1. There are no similar libraries for Android.
  2. The official AWS docs suggest using AWS Amplify and make use of Cognito in all sample codes, which I don’t have in my project.
  3. Some examples show using the AWS-Android-SDK to create an S3Client using credentials stored in the app, which is also not an option for me.

The farthest I've gotten: The option that looked the most promising is the TransferUtility class in the AWS Mobile SDK:

val transferUtility =
   TransferUtility.builder()
      .context(c)
      .awsConfiguration(awsConfiguration)
      .s3Client(s3Client)
      .build()

But where do I get the awsConfiguration and s3Client from? Also is BasicSessionCredentials class somehow related to my problem?

Questions: Is there an actual way to get this started? Is the only way to do this to manually handle the splitting and uploading? Why isn’t there an Uppy alternative yet in Android?


Solution

  • I ended up solving this using AWS Security Token Service. I fetch temporary credentials from our Oauth secured api, avoiding the need to store credentials in the app itself. Then I use the TransferUtility like this:

    val secrets = //fetch from API
    
    val awsCredentials: AWSCredentials = BasicSessionCredentials(secrets.accessKey, secrets.secretKey, secrets.sessionToken)
    val s3Client = AmazonS3Client(awsCredentials, Region.getRegion(Regions.AP_SOUTHEAST_1))
    val awsConfiguration = AWSConfiguration(context)
    val transferUtility = TransferUtility.builder().context(context).awsConfiguration(awsConfiguration).s3Client(s3Client).build()
    val uploadObserver: TransferObserver = transferUtility.upload(media.key, media.file)
    

    Hope this helps anyone coming here in the future.