简体   繁体   中英

Upload a file to AWS S3 pre-signed URL using Retrofit2

I'm trying to upload a file to Amazon's S3 using a pre-signed URL. I get the URL from a server which generates the URL & sends it to me as part of a JSON object. I get the URL as a String, something like this:

https://com-example-mysite.s3-us-east-1.amazonaws.com/userFolder/ImageName?X-Amz-Security-Token=xxfooxx%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fxxbarxx%3D&X-Amz-Algorithm=xxAlgoxx&X-Amz-Date=20170831T090152Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3600&X-Amz-Credential=xxcredxx&X-Amz-Signature=xxsignxx

Unfortunately, when I pass this to Retrofit2, it modifies the String attempting to make it into a URL. I've set encoding=true which took care of most of the problem but not completely. I know the String works as it is. I've tried it in Postman & get a successful response.

1st I tried just putting the String (except for what I cut out as baseUrl) as a whole into the Path

public interface UpdateImageInterface {
    @PUT("{url}")
    Call<Void> updateImage(@Path(value="url", encoded=true) String url, Body RequestBody image);
}

The calling 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);

This works mostly except the the '?' (after "ImageName") get converted to "%3F". This causes a Bad Request / 400.

My next attempt was to create a query with Retrofit2 but then dump the whole String (with 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);
}

The calling 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 gets the '?' rendered correctly but all of the '&' get changed to "%26"

Lastly I tried passing the whole String in baseUrl() but that gives an IllegalArgumentException for not having '/' on the end.

I know that I could parse the pre-signed URL to make multiple queries & assemble them in Retrofit2 as queries should be done but I'd like to avoid that processing.

To restate the question:

Is there a way to easily (without heavy String parsing) upload a file to S3 with a pre-signed URL using Retrofit2?

With help from a colleague, this is the solution.

public interface UpdateImageInterface {
    @PUT
    Call<Void> updateImage(@Url String url, @Body RequestBody image);
}

Calling 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 for info on @Url (class Url) & baseUrl() (class Retrofit.Builder)

MediaType is a class in the OkHttp library that is often used with Retrofit (both from Square). Info about constants passed to the parse method can be found in the Javadoc.

Use the following while uploading directly to S3 using presigned URL.

@Multipart
@PUT
@Headers("x-amz-acl:public-read")
Call<Void> uploadFile(@Url String url, @Header("Content-Type") String contentType, @Part MultipartBody.Part part);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM