【问题标题】:Upload a file to AWS S3 pre-signed URL using Retrofit2使用 Retrofit2 将文件上传到 AWS S3 预签名 URL
【发布时间】:2017-09-01 00:13:30
【问题描述】:

我正在尝试使用预签名 URL 将文件上传到 Amazon 的 S3。我从生成 URL 的服务器获取 URL,并将其作为 JSON 对象的一部分发送给我。我将 URL 作为字符串获取,如下所示:

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

不幸的是,当我将它传递给 Retrofit2 时,它会修改试图使其成为 URL 的字符串。我设置了encoding=true,它解决了大部分问题,但没有完全解决。我知道字符串按原样工作。我已经在 Postman 中尝试过,并获得了成功的响应。

第一次我尝试将字符串(除了我剪切为 baseUrl 的内容)作为一个整体放入路径中

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

调用代码:

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

这主要工作除了'?' (在“ImageName”之后)被转换为“%3F”。这会导致错误请求/400。

我的下一个尝试是使用 Retrofit2 创建一个查询,然后将整个字符串(带有多个查询)转储到查询中。

public interface UpdateImageInterface {
    @PUT("ImageName")
    Call<Void> updateProfilePhoto(@Query(value="X-Amz-Security-Token", encoded = true) String token, @Body RequestBody image);
}

调用代码:

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

这会得到“?”正确渲染,但所有 '&' 都更改为 "%26"

最后,我尝试在 baseUrl() 中传递整个字符串,但这会导致 IllegalArgumentException,因为最后没有“/”。

我知道我可以解析预签名的 URL 以进行多个查询并在 Retrofit2 中组装它们,因为应该完成查询,但我想避免这种处理。

重申问题:

有没有一种方法可以使用 Retrofit2 轻松(无需繁重的字符串解析)将文件上传到 S3,并使用预先签名的 URL?

【问题讨论】:

标签: java android amazon-s3 retrofit2


【解决方案1】:

在同事的帮助下,这就是解决方案。

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

调用代码:

    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 获取有关 @Url 的信息(类 URL)& baseUrl()(Retrofit.Builder 类)

MediaTypeOkHttp 库中的一个类,经常与 Retrofit 一起使用(均来自 Square)。有关传递给 parse 方法的常量的信息可以在 Javadoc 中找到。

【讨论】:

  • 嘿,我也遇到了类似的问题。你是怎么在这里生成requestFile的?
  • @AmitBarjatya 编辑答案以包括 requestFile 的声明。
  • @Gary99,CONTENT_IMAGE 是什么?
  • @Eric 查看更新的答案。添加了 CONTENT_IMAGE 的定义和有关其来源的信息。
  • 大图>8MB的状态怎么样。它会起作用吗?
【解决方案2】:

使用预签名 URL 直接上传到 S3 时使用以下内容。

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

【讨论】:

  • 关于此解决方案@Headers("x-amz-acl:public-read") 的注释仅应谨慎添加,因为它可能导致签名不匹配。
猜你喜欢
  • 2019-08-05
  • 2020-10-23
  • 1970-01-01
  • 2020-09-30
  • 1970-01-01
  • 2021-02-15
  • 1970-01-01
  • 1970-01-01
  • 2019-07-27
相关资源
最近更新 更多