使用Android Retrofit V2库的AWS S3 Rest API,上传的图像已损坏

And*_*n K 8 rest android amazon-s3 retrofit

我正在尝试Image从我的Android APP上传到Amazon AWS S3,我需要使用AWS Restful API.

我正在使用Retrofit 2来提出请求.

我的应用程序与Amazon S3成功连接并按预期执行请求,但是当我尝试ImageBucket查看时,图片无法打开.我下载Image到我的电脑并尝试打开但仍然收到图像已损坏的消息.

让我们看看我的完整代码.

我的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'
Run Code Online (Sandbox Code Playgroud)

这里创建了一个文件并启动了请求

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

改造界面

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);
}
Run Code Online (Sandbox Code Playgroud)

Utils类用于挂载凭据

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后是提出请求的方法

 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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

我感谢任何帮助,提前谢谢!

小智 9

我使用了 Retrofit 2 解决方案,我使用Body而不是Part用于您RequestBody的界面

@PUT("")
Call<String> nameAPI(@Url String url, @Body RequestBody body);
Run Code Online (Sandbox Code Playgroud)

和java代码

// 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()) {
            // Your handling
        } else {
            // Your handling
       }
   }

   @Override
   public void onFailure(Call<String> call, Throwable t) {
       Toast.makeText(getContext(), "onFailure : "+t.getMessage().toString(),Toast.LENGTH_SHORT).show();
   }
});
Run Code Online (Sandbox Code Playgroud)


小智 5

我有同样的问题,当我使用Fiddler检查HTTP请求内容时,我发现改进2.0.0 beta1与1.9.0有所不同.

在我的问题中,不同的HTTP请求内容阻止服务器获取正确的数据.

为了生成相同的HTTP请求内容,我使用retrofit 2.0.0 deta1执行后续步骤.


在改造服务中,为http请求添加表单数据头;

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

int retrofit 2.0.0 deta1,标题使用@Multipart将得到这样的数据:

内容类型:multipart/mixed

由于聋人的价值是混合的,没有边界标题.


不要使用@Multipart上传文件,只需使用@Body RequestBody

如果您使用@Multipart请求服务器,则必须通过param(文件)

@Part(key),那么你将遇到一个新问题.可能是改造2.0.0beta1有一个BUG ...,@Multipart用1.9.0生成一个错误的http请求编译.


调用方法时,需要将MultipartRequestBody传递给@Body RequestBody

使用MultipartBuilder创建MultipartRequestBody,当你新的MultipartBuilder,称之为consturt:

new MultipartBuilder("95416089-b2fd-4eab-9a14-166bb9c5788b")
Run Code Online (Sandbox Code Playgroud)

你设置了param int @headers(boundary=)

builder.addFormDataPart(String name, String filename, RequestBody value)
Run Code Online (Sandbox Code Playgroud)

此方法将有助于形成如下数据的HTTP请求内容:

内容处理:表格数据; NAME = "imgFile"; filename ="IMG_20150911_113029.jpg"内容类型:图像/ jpg内容长度:1179469

RequestBody value是您在代码中生成的值.

我只是临时解决这个问题.

希望可以帮到你!


Bin*_*aby 4

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);
Run Code Online (Sandbox Code Playgroud)

我有同样的经历,并通过https://github.com/square/retrofit/issues/2424这个解决方案解决了它