AWS S3 Rest API with Android Retrofit V2 library, uploaded image damaged

I am trying to upload Image from my Android APP to Amazon AWS S3 and I need to use the AWS Restful API .

I am using Retrofit 2 to make a request.

My application successfully connects to Amazon S3 and executes the request as expected, but when I try to view Image from Bucket , the image does not open. I downloaded Image to my computer and tried to open it, but I continue to receive a message that the image is damaged.

Let's look at my full code below.

My Dependencies Gradle

 compile 'com.squareup.retrofit:retrofit:2.0.0-beta1' compile 'com.squareup.retrofit:converter-gson:2.0.0-beta1' compile 'net.danlew:android.joda:2.8.2' 

Here a file is created and the request is launched

 File file = new File(mCurrentPhotoPath); RequestBody body = RequestBody.create(MediaType.parse("image/jpeg"), file); uploadImage(body, "photo_name.jpeg"); 

Retrofit interface

 public interface AwsS3 { @Multipart @PUT("/{Key}") Call<String> upload(@Path("Key") String Key, @Header("Content-Length") long length, @Header("Accept") String accept, @Header("Host") String host, @Header("Date") String date, @Header("Content-type") String contentType, @Header("Authorization") String authorization, @Part("Body") RequestBody body); } 

Utils class for mounting credentials

 public class AWSOauth { public static String getOAuthAWS(Context context, String fileName) throws Exception{ String secret = context.getResources().getString(R.string.s3_secret); String access = context.getResources().getString(R.string.s3_access_key); String bucket = context.getResources().getString(R.string.s3_bucket); return gerateOAuthAWS(secret, access, bucket,fileName); } private static String gerateOAuthAWS(String secretKey, String accessKey, String bucket, String imageName) throws Exception { String contentType = "image/jpeg"; DateTimeFormatter fmt = DateTimeFormat.forPattern("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z").withLocale(Locale.US); String ZONE = "GMT"; DateTime dt = new DateTime(); DateTime dtLondon = dt.withZone(DateTimeZone.forID(ZONE)).plusHours(1); String formattedDate = dtLondon.toString(fmt); String resource = "/" + bucket + "/" + imageName; String stringToSign = "PUT" + "\n\n" + contentType + "\n" + formattedDate + "\n" + resource; Mac hmac = Mac.getInstance("HmacSHA1"); hmac.init(new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA1")); String signature = ( Base64.encodeToString(hmac.doFinal(stringToSign.getBytes("UTF-8")), Base64.DEFAULT)).replaceAll("\n", ""); String oauthAWS = "AWS " + accessKey + ":" + signature; return oauthAWS; } } 

Finally, the method to query

  public void uploadImage(RequestBody body, String fileName){ String bucket = getString(R.string.s3_bucket); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://" + bucket + ".s3.amazonaws.com") .addConverterFactory(GsonConverterFactory.create()) .build(); AwsS3 service = restAdapter.create(AwsS3.class); DateTimeFormatter fmt = DateTimeFormat.forPattern("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z").withLocale(Locale.US); String ZONE = "GMT"; DateTime dt = new DateTime(); DateTime dtLondon = dt.withZone(DateTimeZone.forID(ZONE)).plusHours(1); String formattedDate = dtLondon.toString(fmt); try { String oauth = AWSOauth.getOAuthAWS(getApplicationContext(), fileName); Call<String> call = service.upload(fileName, body.contentLength(), "/**", bucket + ".s3.amazonaws.com", formattedDate, body.contentType().toString(), oauth, body); call.enqueue(new Callback<String>() { @Override public void onResponse(Response<String> response) { Log.d("tag", "response : " + response.body()); } @Override public void onFailure(Throwable t) { Log.d("tag", "response : " + t.getMessage()); } }); } catch (Exception e) { e.printStackTrace(); } } 

I appreciate any help, thanks in advance!

+6
android rest amazon-s3 retrofit
Sep 19 '15 at 1:45
source share
6 answers
 RequestBody avatarBody = RequestBody.create(MediaType.parse("image"),file); MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), avatarBody); @Multipart @POST(url) Call<ResponseBody> uploadImageAmazon( @Part MultipartBody.Part filePart); 

I had the same experience and solved it https://github.com/square/retrofit/issues/2424 this solution

+1
Feb 24 '18 at 7:07
source share

You submit a multi-page payload, but you force the Content-type to be image/jpeg . Your jpg is corrupted because S3 probably saved the multi-page headers in your jpg file since you said that the whole message was JPG. Since you actually do not have multiple parts to send, you can leave a Multipart annotation and use Body instead of Part for your RequestBody

 public interface AwsS3 { @PUT("/{Key}") Call<String> upload(@Path("Key") String Key, @Header("Content-Length") long length, @Header("Accept") String accept, @Header("Host") String host, @Header("Date") String date, @Header("Content-type") String contentType, @Header("Authorization") String authorization, @Body RequestBody body); } 

You should also be able to explicitly remove the Content-type and Content-length headers.

+3
Sep 24 '15 at 19:41
source share

I have the same problem, and as I use Fiddler, I checked the contents of the HTTP request, I found that retrofit 2.0.0 beta1 is different from 1.9.0.

In my problem, the different content of the HTTP request does not allow the server to receive the correct data.

To make the same HTTP request content, I follow these steps with retrofit 2.0.0 deta1.




In the retrofit service, add the header of the data form for the http request;

 @Headers("Content-Type: multipart/form-data;boundary=95416089-b2fd-4eab-9a14-166bb9c5788b") 

int retrofit 2.0.0 deta1 , the header using @Multipart will get this data:

Content-Type: multipart / mixed

since the meaning of deafness is mixed and has no name for boundaries.




Do not use @Multipart to upload a file, just using @Body RequestBody

if you use @Multipart to request a server, you need to pass the parameter (file) via

@Part(key) , then you get a new problem. Maybe retrofit 2.0.0beta1 has BUG ..., @Multipart generate a bad http request compiled with 1.9.0.




When you call the method, you need to pass MultipartRequestBody to @Body RequestBody

Using MultipartBuilder to create a MultipartRequestBody , when you are a new MultipartBuilder , call this consturt:

 new MultipartBuilder("95416089-b2fd-4eab-9a14-166bb9c5788b") 

the parameter you set int @headers(boundary=)

 builder.addFormDataPart(String name, String filename, RequestBody value) 

This method will help to generate data such as the following int int request request:

Content-Disposition: form-data; name = "imgFile"; filename = "IMG_20150911_113029.jpg" Content-Type: image / jpg Content-length: 1179469

RequestBody Value is what you created in your code.

I just solve this problem temporarily.

Hope can help you!

+3
Sep 26 '15 at 11:22
source share

I did not use Retrofit 2, just Retrofit 1, so YMMV, but I think the typical way to do what you are trying to do is to use TypedFile, where you are trying to use RequestBody.

I assume Retrofit uses RequestBody internally.

You would create a TypedFile something like:

 TypedFile typedFile = new TypedFile("multipart/form-data", new File("path/to/your/file")); 

and your interface will be:

  @Multipart @PUT("/{Key}") Call<String> upload(@Path("Key") String Key, @Header("Content-Length") long length, @Header("Accept") String accept, @Header("Host") String host, @Header("Date") String date, @Header("Content-type") String contentType, @Header("Authorization") String authorization, @Part("Body") TypedFile body); } 

There's a decent example at https://futurestud.io/blog/retrofit-how-to-upload-files/

+1
Sep 22 '15 at 2:41
source share

You can use retrofit 2 to upload image / file

 @Multipart @POST("/api/attachment") Call<JsonPrimitive> addAttachment(@Part MultipartBody.Part imageFile); 

Now to call:

  RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); MultipartBody.Part imageFileBody = MultipartBody.Part.createFormData("file", file.getName(), requestBody); 

Note. Make sure you are using retrofit2, because for some reason I could not load the image using the retrofit1 library.

0
Sep 15 '17 at 13:19 on
source share
 I have used Retrofit 2 resolve
 and I use Body instead of Part for your RequestBody in interface
 @PUT("")Call<String> nameAPI(@Url String url ,@Body RequestBody body); 

and java code

  //prepare image file File file = new File(pathImg); RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpeg"), file); Call<String> call = SingletonApiServiceS3.getInstance().getService().nameAPI( path, requestBody ); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, final Response<String> response) { if (response.isSuccessful()) { } else { } } @Override public void onFailure(Call<String> call, Throwable t) { Toast.makeText(getContext(),"onFailure : "+t.getMessage().toString(),Toast.LENGTH_SHORT).show(); } }); 
0
Oct 05 '17 at 6:33
source share



All Articles