Android HttpURLConnection PUT для Amazon AWS S3 403 ошибка

Загружая файл в Amazon S3 с использованием заранее заданного URL-адреса, который имеет Signature, Expiration и AccessKey, используя следующий код, я могу загрузить файл, используя обычный код Java, но тот же код в Android вызывает ошибку 403. Предопределенный URL-адрес создается с помощью Amazon SDK

Я прочитал http://developer.android.com/reference/java/net/HttpURLConnection.html и http://android-developers.blogspot.com/2011/09/androids-http-clients.html но не смог выяснить, какой параметр заголовка я должен использовать, я думаю, в Android это установка заголовков в запросе, который сервер отклоняет

    HttpURLConnection connection=(HttpURLConnection) url.openConnection();
    connection.setDoOutput(true);
    connection.setRequestMethod("PUT"); 
    OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
    out.write("This text uploaded as object.");
    out.close();
    int responseCode = connection.getResponseCode();

Исключение: 403; Подпись не совпадает:-o

Кто-нибудь сталкивался с этой проблемой? Или более подробно, какие параметры заголовка добавляются за кулисами из библиотеки Android?

5 ответов

Пожалуйста, установите ваш тип контента следующим образом:

connection.setRequestProperty("Content-Type"," ");

потому что по умолчанию для HttpsUrlConnection автоматически генерируется тип контента как:

"Content-Type: application/x-www-form-urlencoded"

Это приведет к несоответствию подписи.

Итак, после некоторых проб / ошибок / поиска выяснили проблему:

При создании предварительно назначенного URL важно указать Content-Type (на основе ваших данных), иначе вы будете получать 403 Signature не совпадают, указанный вами тип contentType должен быть упомянут в объекте соединения HttpURLConnection.

    string s3url = s3Client.GetPreSignedURL(new GetPreSignedUrlRequest()
                   .WithBucketName(bucketName)
                   .WithKey(keyName)
                   .WithContentType("application/octet-stream") // IMPORTANT
                   .WithVerb(HttpVerb.PUT)
                   .WithExpires(<YOUR EXPIRATION TIME>);

Внутри вашего соединения... добавьте это к соответствующему коду после ("PUT")

    connection.setFixedLengthStreamingMode(< YOUR DATA.LENGTH ?>);        
    connection.setRequestProperty("Content-Type","application/octet-stream");

Проверьте эту ошибку, последний SDK должен позволить вам установить Content-Type

Способ создания URL:

private static URL generateRUL(String objectKey, String ACCESS_KEY, String SECRET_KEY, String BUCKET_NAME) {
    AmazonS3 s3Client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
    URL url = null;

    try {
        GeneratePresignedUrlRequest request  = new GeneratePresignedUrlRequest(BUCKET_NAME, objectKey);
        request.setMethod(com.amazonaws.HttpMethod.PUT);
        request.setExpiration(new Date( System.currentTimeMillis() + (60 * 60 * 1000)));

        // Very important ! It won't work without adding this! 
        // And request.addRequestParameter("Content-Type", "application/octet-stream") won't work neither
        request.setContentType("application/octet-stream");

        url = s3Client.generatePresignedUrl(request ); 
    } catch (AmazonServiceException exception) { 
    } catch (AmazonClientException ace) { }

    return url;
}

Способ загрузки файла:

public int upload(byte[] fileBytes, URL url) {
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setDoOutput(true);
    connection.setRequestMethod("PUT");
    connection.setRequestProperty("Content-Type", "application/octet-stream"); // Very important ! It won't work without adding this!
    OutputStream output = connection.getOutputStream();

    InputStream input = new ByteArrayInputStream(fileBytes);
    byte[] buffer = new byte[4096];
    int length;
    while ((length = input.read(buffer)) > 0) {
        output.write(buffer, 0, length);
    }
    output.flush();

    return connection.getResponseCode();
}

У меня была такая же проблема. Вот причина, почему я столкнулся с этим:

Предварительно подписанный URL-адрес предоставляет вам доступ к объекту, указанному в URL-адресе, при условии, что создатель предварительно подписанного URL-адреса имеет разрешения на доступ к этому объекту. То есть, если вы получили предварительно подписанный URL-адрес для загрузки объекта, вы можете загрузить объект только в томслучае, если создатель предварительно подписанного URL-адреса имеет необходимые разрешения для загрузки этого объекта.

http://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html

Как только я дал моей лямбда-функции полномочия PutObject для этого сегмента s3, он заработал!

Используете ли вы AWS SDK для Android? У него правильные API, и я подозреваю, что будет проще загрузить его на S3, используя его, и в этом отношении он будет более безопасным. Я считаю, что у них там есть учебники, примеры кода и демонстрации.

Существует также API для S3 и других классов, которые могут вам понадобиться. PutObjectRequest - это то, что поможет вам загрузить файл с телефона-клиента и может быть полезно в вашем случае.

Другие вопросы по тегам