如何在 Android 中使用 REST API 将图像上传到 S3 存储桶,出现禁止错误:403

Posted

技术标签:

【中文标题】如何在 Android 中使用 REST API 将图像上传到 S3 存储桶,出现禁止错误:403【英文标题】:How to upload an image to S3 bucket using REST API in Android, Getting Forbidden Error: 403 【发布时间】:2018-10-19 15:30:06 【问题描述】:

我想在不使用 Amazon SDK 的情况下使用 REST API 将媒体数据上传到 S3 存储桶。我在将文件上传到 s3 存储桶时收到“403 Forbidden Access Denied Error”,请建议合适的操作。如果还需要什么,我也可以分享。

String bucket = getString(R.string.s3_bucket);
            RestAdapter.Builder builder = new RestAdapter.Builder()
                .setEndpoint("http://" + bucket + ".s3.amazonaws.com")
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setClient(new OkClient(new OkHttpClient()));

            AwsS3 aws = builder.build().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 dateTime = dt.withZone(DateTimeZone.forID(ZONE)).plusHours(1);
            String formattedDate = dateTime.toString(fmt);

            try 
                Bitmap bm = BitmapFactory.decodeFile(mCurrentPhotoPath);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); //bm is the bitmap object
                byte[] b = baos.toByteArray();
                TypedInput body = new TypedByteArray("image/jpg", b);
                String imageName = "files_" + System.currentTimeMillis();
                String oauth = AWSOauth.getOAuthAWS(getApplicationContext(), imageName.trim());
                String host = bucket + ".s3.amazonaws.com";
                String mimeType = body.mimeType();
                long length = body.length();
                File file = new File(mCurrentPhotoPath);
                RequestBody bb = RequestBody.create(MediaType.parse("image/jpeg"), file);

                aws.upload(imageName.trim(),length,"/**",host,formattedDate,mimeType,oauth,bb,new Callback<String>()

                    @Override
                    public void success(String s, Response response) 
                        Log.d("tag","S = " + s);
                        Log.d("tag","getHeaders = " + response.getHeaders());
                        Log.d("tag","Status = " + response.getStatus());
                    

                    @Override
                    public void failure(RetrofitError error) 
                        Log.d("tag","error: S = " + error.getMessage());
                    
                );
            catch (Exception e)

            


 @PUT("/Key")
void 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,Callback<String> mCallback);

【问题讨论】:

你找到解决办法了吗? 【参考方案1】:

如果您的应用程序具有关联的后端服务,您可以在 android 应用程序中无需 Amazon SDK 的情况下上传到 S3,前提是您获得后端生成的上传内容的预签名 URL,并通过 API 调用将其传送到应用程序。以下是亚马逊针对不同语言的一些文档:https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html

以下是一些使用来自后端的预签名 URL 的 Android 代码示例:

public interface AmazonService 
    @Multipart
    @POST
    Call<ResponseBody> upload(@Url String url, @PartMap Map<String,RequestBody> params, @Part MultipartBody.Part file);


public class AmazonS3Client 
    private static final String EMPTY_URL_TO_MAKE_RETROFIT_HAPPY = "";

    public boolean uploadMoment(Map<String, String> fields, String filePath, String s3BaseUrl) 
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
        OkHttpClient okClient = new OkHttpClient.Builder().addInterceptor(interceptor).build();
        Retrofit retrofit = new Retrofit.Builder().client(okClient).baseUrl(s3BaseUrl + "/").build();

        RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), new File(filePath));
        MultipartBody.Part body = MultipartBody.Part.createFormData("file", fields.get("key"), requestFile);
        Map<String, RequestBody> parameters = new HashMap<>();
        for (Map.Entry<String, String> entry : fields.entrySet()) 
            parameters.put(entry.getKey(), createPartFromString(entry.getValue()));
        

        AmazonService service = retrofit.create(AmazonService.class);
        Call<ResponseBody> call = service.upload(EMPTY_URL_TO_MAKE_RETROFIT_HAPPY, parameters, body);
        try 
            Response<ResponseBody> execute = call.execute();
            if (execute.code() == 204) 
                return true;
             else 
                Log.e("AmazonS3Client", "unexpected http response: " + execute.code());
            
         catch (IOException e) 
            Log.e("AmazonS3Client", "upload error", e);
        
        return false;
    

    private RequestBody createPartFromString(String descriptionString) 
        return RequestBody.create(okhttp3.MultipartBody.FORM, descriptionString);
    

【讨论】:

您能否分享更多细节。 抱歉,我无法为您提供完全编程的解决方案,但我添加了一个预签名 URL 的 AWS 文档链接,以便在您的后端实现它,以及我用来拨打电话的类示例在 Android 客户端中使用来自后端的给定值进行改造。【参考方案2】:
 RestAdapter.Builder builder = new RestAdapter.Builder()
                .setEndpoint("http://" + bucket + ".s3.amazonaws.com")
                .setLogLevel(RestAdapter.LogLevel.FULL)
*<add this line>>>>>*.setRequestInterceptor(new AuthToken())
                .setClient(new OkClient(new OkHttpClient()));

你的 AuthToken 会是这样的:-

public class AuthToken implements RequestInterceptor
    
            @Override
            public void apply(RequestFacade request)
        
            var authTokenString = AWS <Name>:<Key>
            request.addHeader("Authorization", authTokenString);
        
    

【讨论】:

String oauth = AWSOauth.getOAuthAWS(getApplicationContext(), imageName.trim()); RestAdapter.Builder builder = new RestAdapter.Builder() .setEndpoint("http://" + bucket + ".s3.amazonaws.com") .setLogLevel(RestAdapter.LogLevel.FULL) .setRequestInterceptor(new AuthToken(oauth)) .setClient(new OkClient(new OkHttpClient())); 公共类 AuthToken 实现 RequestInterceptor String oauth; public AuthToken(String oauth) this.oauth = oauth; @Override public void intercept(RequestFacade request) // authTokenString = AWS : request.addHeader("Authorization", oauth);我得到错误retrofit.RetrofitError: 501 Not Implemented

以上是关于如何在 Android 中使用 REST API 将图像上传到 S3 存储桶,出现禁止错误:403的主要内容,如果未能解决你的问题,请参考以下文章

如何在android中执行并行REST API调用

使用 REST API 在 Android 应用上使用 Paypal 登录

如何唯一标识您的 Android 应用程序以获取 REST API

如何在Woocommerce Rest API中获取Tokenization密钥?

如何从 android 客户端进行经过身份验证的 django rest api 调用?

REST API 发送 JWT