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

对着背影说爱祢 提交于 2020-01-11 10:01:47

问题


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?


回答1:


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.




回答2:


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


来源:https://stackoverflow.com/questions/45991720/upload-a-file-to-aws-s3-pre-signed-url-using-retrofit2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!