Use Retrofit2 to upload files to the AWS S3 presigned URL
I’m trying to upload a file to Amazon’s S3 using a presigned URL. I get the URL from the server that generated the URL and send it to me as part of a JSON object. I get the URL as a string like this:
Unfortunately, when I pass it to Retrofit2, it modifies the string that tries to turn it into a URL. I’ve set encoding=true
to fix most of the issues, but haven’t quite solved it yet. I know String works as-is. I’ve tried it in Postman and got a successful response.
1st I tried putting the string (except what I removed as baseUrl) into the path as a whole
public interface UpdateImageInterface {
@PUT("{url}")
Call<Void> updateImage(@Path(value="url", encoded=true) String url, Body RequestBody image);
}
Call code:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://com-example-mysite.s3-us-east-1.amazonaws.com/userFolder/")
.build();
UpdateImageInterface imageInterface = retrofit.create(UpdateImageInterface.class);
imageUrl is "ImageName..."
Call<Void> call = imageInterface.updateImage(imageUrl, requestFile);
Apart from ‘?’, this is mostly valid (after “ImageName”) to “%3F”. This results in Bad Request/400.
My next attempt was to create a query with Retrofit2 and dump the entire string (containing multiple queries) into the query.
public interface UpdateImageInterface {
@PUT("ImageName")
Call<Void> updateProfilePhoto(@Query(value="X-Amz-Security-Token", encoded = true) String token, @Body RequestBody image);
}
Call code:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://com-example-mysite.s3-us-east-1.amazonaws.com/userFolder/")
.build();
UpdateImageInterface imageInterface = retrofit.create(UpdateImageInterface.class);
imageUrl is "xxfooxx..."
Call<Void> call = imageInterface.updateImage(imageUrl, requestFile);
This will get ‘?’ Render correctly, but all “&” is changed to “%26”
Finally, I tried passing the entire string in baseUrl()
but gave IllegalArgumentException because there was no “/” at the end.
I
know I can parse presigned URLs for multiple queries and combine them in Retrofit2 because the queries should be done, but I want to avoid this processing.
Restate the question:
Is there a way to easily (without heavy string parsing) to S3 using pre-signed URLs using Retrofit2?
Solution
With the help of colleagues, this is the solution.
public interface UpdateImageInterface {
@PUT
Call<Void> updateImage(@Url String url, @Body RequestBody image);
}
Call code:
String CONTENT_IMAGE = "image/jpeg";
File file = new File(localPhotoPath); create new file on device
RequestBody requestFile = RequestBody.create(MediaType.parse(CONTENT_IMAGE), file);
/* since the pre-signed URL from S3 contains a host, this dummy URL will
* be replaced completely by the pre-signed URL. (I'm using baseURl(String) here
* but see baseUrl(okhttp3. HttpUrl) in Javadoc for how base URLs are handled
*/
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.dummy.com/")
.build();
UpdateImageInterface imageInterface = retrofit.create(UpdateImageInterface.class);
imageUrl is the String as received from AWS S3
Call<Void> call = imageInterface.updateImage(imageUrl, requestFile);
Javadoc information about @Url
(class URLs) &
baseUrl() (
class Retrofit.Builder).
MediaType
is a library in a class in OkHttp that is often used with Retrofit (both from Square). Information about constants passed to the parser method can be found in Javadoc.