0

I'm trying to upgrade my code base from Retrofit 1.9 to version 2.0. I have no problem in upload of an image in version 1.9. However I'm getting following S3 response error after I changed it to version 2.0.

This is my interface to upload image in Retrofit v1.9:

public interface IFileUploadAPI
{
    @PUT("/{url}")
    void uploadFile(@Path(value = "url", encode = false) String url, @Body() TypedFile file,
                    Callback<UploadFileResponse> callback);
}

I've changed it to following code on Retrofit v2.0:

public interface IFileUploadAPI
{
    @PUT
    Call<UploadFileResponse> uploadFile(@Url String url, @Body RequestBody file);
}

And I'm implementing it in this way.

public class MyFileUploadAPI
{
    private IFileUploadAPI mService;

    public MyFileUploadAPI()
    {
        final Retrofit adapter = new Retrofit.Builder() //
                        .baseUrl(MyAPIConstant.API_URL_BASE) // This URL will be ignored as we are using dynamic url. So no matters what it is.
                        .addConverterFactory(GsonConverterFactory.create(GsonUtils.getGson())) //
                        .client(ASDKApplication.getInstance().getOkHttpClient()) //
                        .build();

        this.mService = adapter.create(IFileUploadAPI.class);
    }

    public void uploadFile(final String url, final String filePath, final String mimeType)
    {
        RequestBody file = RequestBody.create(MediaType.parse(mimeType), filePath);
        Call<UploadFileResponse> call = mService.uploadFile(url, file);
                call.enqueue(new Callback<HitchUploadFileResponse>()
        {
            @Override
            public void onResponse(Call<UploadFileResponse> call, Response<UploadFileResponse> response)
            {
                UploadFileResponse uploadFileResponse = new UploadFileResponse();

                if (response.isSuccessful() && response.body() != null)
                {
                    uploadFileResponse = response.body();
                }

                // Rest of code...
            }

            @Override
            public void onFailure(Call<UploadFileResponse> call, Throwable t)
            {
                Logger.error(TAG, "uploadFile.onFailure(), msg: " + t.getMessage());
            }
        }
    }
}

So, when user click on Upload button, I call an API and it returns me a url that I have to call. The response looks likes:

[{"uploadSignedURL":"https://my-account.s3.amazonaws.com/license/1000_front_1464988248.jpeg?AWSAccessKeyId=MY_ID\u0026Expires=1464991848\u0026Signature=NhStdEX8RH3J0uzvVa6h%2FvN6FZQ%3D","filePath":"license/1000_front_1464988248.jpeg","target":"driving_license_front_img"}]

And finally the response to call of above url (http status code 403 forbidden):

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>MY_ID</AWSAccessKeyId><StringToSign>PUT

image/jpeg; charset=utf-8
1464991848
/my-account/license/1000_front_1464988248.jpeg</StringToSign><SignatureProvided>NhStdEX8RH3J0uzvVa6h/vN6FZQ=</SignatureProvided><StringToSignBytes>50 55 54 0a 0a 69 6d 61 67 65 2f 6a 70 65 67 3b 20 63 68 61 72 73 65 74 3d 75 74 66 2d 38 0b 31 34 36 34 39 39 31 38 34 38 0a 2f 6d 79 74 65 6b 73 69 3a 67 72 61 62 68 69 45 63 68 2f 6c 69 63 65 6e 73 65 2f 31 30 30 30 5f 66 72 6f 6e 74 5f 31 34 36 34 39 38 38 32 34 38 2e 6a 70 65 67</StringToSignBytes><RequestId>2D6D08FE4E34FCE5</RequestId><HostId>tpvmJdeRxtAzPxOk2zFwMF2Ne6hmNMkcaM9D7m/I13iynYGJyqtC0U72B2t41SDYfPBjCRTVyOY=</HostId></Error>
halfer
  • 19,824
  • 17
  • 99
  • 186
Hesam
  • 52,260
  • 74
  • 224
  • 365

1 Answers1

0

Ok, I found what's wrong.

okhttp is adding charset=utf-8 into my contentType. Therefore my contentType becomes like this contentType: image/jpeg; charset=utf-8. I tested on Postman and by delete of ; charset=utf-8 I'm able to upload my image.

https://stackoverflow.com/a/25560731/513413 says the reason of adding charset. It's because I'm passing path to my image file. You can easily change

RequestBody file = RequestBody.create(MediaType.parse(mimeType), filePath);

to

RequestBody file = RequestBody.create(MediaType.parse(mimeType), new File(filePath));

There is one more important thing. Once you upload your image to S3, Amazon returns HTTP status code 200 with empty body. So you have to change the interface too.

public interface IFileUploadAPI
{
    @PUT
    Call<Void> uploadFile(@Url String url, @Body RequestBody file);
}
Community
  • 1
  • 1
Hesam
  • 52,260
  • 74
  • 224
  • 365