How to upload images to AWS S3 from the Android app.
Cloud storage is becoming a must nowadays. Recently I came across a scenario to upload images from an android app to an AWS S3 bucket. Since there is not any official documentation for this implementation I had to do a lot of research on it and came up with a customized solution.
Let’s get started…
Prerequisites
To save time, I’m going to assume that everyone has the prerequisites listed below set up.
- Creating an AWS account(All of the following operations can be done using free tier).
- Having an Android project(Kotlin) created with an empty activity.
Once you are done with the implementation the app will look something like this.
Things to be covered:
- Setting up an S3 bucket.
- Setting up IAM policy and User.
- Setting up a simple UI layout to select and upload images.
- Installing AWS dependencies.
- Adding AWS security credentials.
- Configuring AWS security credentials.
- Method to select an image from the gallery.
- Setting image preview and converting selected image to a file.
- uploading to s3 bucket
1. Setting up an S3 bucket
In the AWS console select S3 and choose to create a bucket, give it a unique name, and keep the other configurations as default.
2. Setting up IAM policy and role for the bucket.
To give permission to your app to access the S3 bucket, you need to apply certain access policies to that bucket. To do that, first navigate to the services menu and choose IAM. Inside the IAM dashboard, select ‘Policies’ and create a new policy. A policy defines permissions for actions on resources or services.
Here you need to create a policy that allows adding a file to the S3 bucket. For that, you have to choose the service as S3 and under the actions menu, since you need to give access to write something to the bucket, you have to choose ‘PutObject’ under the ‘Write’ access level. As you are only going to use this bucket to upload the image, this particular access permission is enough. Likewise, there are so many actions that can be chosen based on our requirements.
Thereafter, you have to specify for which bucket (resource) you are going to apply the policy, because otherwise if you don’t specify any bucket, this policy will be applied to all the buckets in your S3 service, which is not a good practice. And when it comes to objects, then you have to let this policy apply to all the objects inside this bucket.
Then follow the next steps, give a unique name to the policy, and create it.
If policy defines what can be done on a resource, then “user” defines who can access that resource. For that, you have to create an IAM user.
When choosing the AWS access type for the user, it must be given programmatic access because the user in this case would be the mobile app that is going to communicate with the service programmatically.
In the next step, choose the policy that you want this user to have access to. You can choose multiple policies based on the requirements. For this user, it is only the policy that you created earlier that would be enough.
Follow the next steps to create the user. Once we create the user it will display the access key and secret key for the user which are actually the credentials to access the service through this user. Here at this point save those credentials(Remember you won’t get to view the secret key here after so make sure you copy this at this point)in a secured location.
3. Setting up the UI
Here I have designed a simple UI that will let the user upload the image by clicking the image icon (on the image view). It will also let the user choose the image from the file storage (image location) and once the upload button is clicked, the uploading process will begin. Anyway, you can design the UI as you wish.
4. Installing AWS dependencies
implementation("com.amazonaws:aws-android-sdk-s3:2.17.1")
Add this AWS-SDK dependency to your gradle. scripts.
5. Adding AWS credentials
Store the above values as constants.
6. Configuring AWS credentials.
Create an activity and set the UI as the view of it. Thereafter import the credentials from the constant file. Configure them as follow.
To access the S3 bucket first, you need to create an instance of it, to create an instance of it you need to pass the credentials of the USER that has access to that S3 bucket you have created earlier.
BasicAWSCredintials is a class that implements an interface called AWSCredintials which Provides access to the AWS credentials used for accessing AWS services.
An instance of BasicAWSCredentials can be created by passing the AccessID and SecretKey as arguments to its constructor, and by passing the BasicAWSCredinitials instance as the argument to the constructor of AWSS3Client, you can create an instance of the S3 bucket.
7. Method to select an image to upload.
An intent is used to perform an action on the screen. Here, the action we want the app to perform is to pick an image from the device storage. Since the file type you are going to access is an image, the type of intent must be set to ‘images’ only.
Also, note that you are not restricting the images to any specific file types.The reason for mentioning the type as ‘image/*’ is that any type of image file can be accessed. If you want to pick any specific type of image file, like ‘JPEG’ then mention it as ‘image/jpeg’.
Thereafter, you have to specify the action that the intent is expected to perform. As you need to retrieve files from the device storage, you have to specify the type of action (ACTION_OPEN_DOCUMENT), that lets the user pick an image/file from the storage. Thereafter, you have to call the method startActivityForResult() by passing the intent and the request code (will be explained later) as arguments.
8. Setting image preview and creating an image file.
The method startActivityForResult() is used to launch an activity for which you would like to obtain a result when it has finished. When this activity exits, the onActivityResult() method will be triggered with the given request code. The request code can be any int value. The request code identifies the return result when it arrives.
Once you have picked the image, The onActivityResult() method will be triggered and here you have to confirm that the result received is from the activity that invoked the image picking action by checking whether the returned request code and the activity’s request code are the same or not, the successful execution of the activity(result code), and the successful retrieval of data(Uri) through the intent also checked.
The received intent will consist of the content path of the image file (Uri). Here, don’t misunderstand Uri as the file path. The Content URI includes the symbolic name of the entire provider and a name that points to a table (a path), while the file path indicates the absolute location of the file. Using the content URI of the file, you can set the image that has been chosen as the image view.
At this point, if all works well, then you should be able to see the image you selected on the screen. To upload this image, we have to convert it to a file. The image file can be easily created with the file path of the image by passing it as an argument to the constructor File(). But here you don’t have the file path. Instead of that, you have the content URI, which is the content path of the image. To create a file using a content URI, first you have to read it from its location and then open a stream of the content associated with that URI.
The contentResolver is used to read the file stream using the content URI. The role of contentResolver here is to get you access to the image based on the given URI. You don’t need to know the filename or other properties of the file, you just need to pass the URI into openInputStream() to access the image and read it. Now you have to write the stream of bytes that have been read earlier to a file. So before writing it you need to create an empty file.
File.createTempFile(prefix,suffix) creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its name. The name of the image you upload will be decided according to the prefix and suffix you specify. It would be better if you could find a unique naming pattern for each image you upload. After the empty file is created, you have to write the stream of bytes to that file.
For that, you have to create a stream pipeline for that file using FileOutputStream(), which will create a file output stream to write to the file represented by the specified file object inside the parameter and write the data to the file using the created outputStream by passing the bytes of data (byte array) from the inputstream which has the image stream stored in it.
9. Upload the image to the AWS S3 bucket
Finally, we are into the photo uploading part. First, you have to create an instance of the TransferUtility class using the instance of the S3 bucket that you configured earlier. TransferUtility provides a simple API for uploading and downloading content from Amazon S3. You can use the upload() method of the transfer utility to upload the image by passing the bucket name, key-value (will be the name of the image file in the S3 bucket), and the image file.
To keep track of the upload status, you have to set a listener, which is an instance of the TransferObserver class that will notify when progress or state changes. The onStatechanged() callback of the observer is used to notify whether the transfer was successful or failed, while the onProgressChanged() callback is used to keep track of the number of bytes that have been transferred. Here I have set the image view to the default image once the transfer is completed. And finally, the onError callback is used for error handling.
Note: Remember that here we are uploading the image directly from the app without any server interaction.
I hope this helped you wrap your head around uploading images to the S3 bucket from Android.
Thank You!!