Загрузить на s3 с помощью curl, используя предварительно подписанный URL (получая 403)

Я использую curl для вызова API Java ReST для получения URL. Затем Java генерирует предварительно подписанный URL-адрес для загрузки S3, используя мои учетные данные S3, и возвращает его в ответе ReST. Curl берет URL-адрес и использует его для загрузки на S3, но S3 возвращает 403 «Рассчитанная нами подпись запроса не соответствует предоставленной вами подписи. Проверьте свой ключ и метод подписи».

Вот код, который я использую для создания предварительно подписанного URL:

public class S3Util {
    static final AmazonS3 s3 = new AmazonS3Client( new AWSCredentials() {
        @Override
        public String getAWSAccessKeyId() {
            return "XXXXXXX";
        }
        @Override
        public String getAWSSecretKey() {
            return "XXXXXXXXXXXXXX";
        }
    });
    static final String BUCKET = "XXXXXXXXXXXXXXXXXXXXXXXXXXX";

    static public URL getMediaChunkURL( MediaChunk mc, HttpMethod method ) {
        String key = ...
        //way in the future (for testing)...
        Date expiration = new Date( System.currentTimeMillis() + CalendarUtil.ONE_MINUTE_IN_MILLISECONDS*60*1000 );

        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, method);
        req.setExpiration(expiration);
        req.addRequestParameter("Content-Type", "application/octet-stream");

        //this gets passed to the end user:
        return s3.generatePresignedUrl(req);
    }
}

и в curl, запустить из Bash, я выполняю это:

echo Will try to upload chunk to ${location}
curl -i -X POST \
        -F 'Content-Type=application/octet-stream' \
        -F "[email protected]${fileName}" \
        ${location} || (echo upload chunk failed. ; exit 1 )

Помимо прочего, я попробовал PUT и «Content-type» (строчная буква T). Я понимаю, что упускаю что-то (или что-то) очевидное, но после прочтения соответствующих документов, поиска в Google и просмотра множества похожих вопросов я не уверен, что это такое. Я вижу много подсказок о необходимых заголовках, но я думал, что отказавшийся URL-адрес должен был устранить эти потребности. Возможно, нет?

ТИА!

Обновить:

Просто чтобы быть ясно, я протестировал загрузки, и это прекрасно работает.

Java выглядит так:

GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, HttpMethod.GET);
req.setExpiration(expiration);

а curl это просто:

curl -i ${location}

Ответы на вопрос(4)

вам нужно поместить url в одинарные кавычки, иначе половина строки запроса будет обрезана (часть с ключом / подписью).

Решение Вопроса

ить его черезлокон как и ожидалось. Учитывая мои тесты, я подозреваю, что вы действительно не используетелокон правильно - я смог загрузить файл так:

curl -v --upload-file ${fileName} ${location}

Параметр-v выводит заголовки запроса и ответа (а также рукопожатие SSL) для целей отладки и иллюстрации:

> PUT [...] HTTP/1.1
> User-Agent: curl/7.21.0 [...]
> Host: [...]
> Accept: */*
> Content-Length: 12
> Expect: 100-continue

Обратите внимание, что--upload-file (или же-T) облегчаетPUTкак и ожидалось, но при необходимости добавляет больше заголовков, получая в ответ правильный ответ:

< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< x-amz-id-2: [...]
< x-amz-request-id:  [...]
< Date: Tue, 31 Jan 2012 18:34:56 GMT
< ETag: "253801c0d260f076b0d5db5b62c54824"
< Content-Length: 0
< Server: AmazonS3
 omnomnom28 янв. 2017 г., 17:01
Для меня использование предварительно подписанных URL с POST не работало. Переключение только метода (как в GeneratePresignedUrlRequest, так и в curl) решило проблему.
 Kirk Woll17 мар. 2014 г., 22:52
Для меня предварительно подписанные URL-адреса содержали символы, которые требовали от меня заключить весь URL в кавычки. Как только я это сделал, ваш ответ сработал отлично.
 Bjorn Roche31 янв. 2012 г., 21:30
Спасибо за это. Я придерживаюсь своего решения, потому что оно сначала работает, но я отмечаю это как правильное, потому что оно обращается к исходному запросу, и, честно говоря, я думаю, что оно лучше.

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();
}
 Jason24 июл. 2017 г., 04:17
request.setContentType ( "Применение / октет-поток"); оно работает! Я получил 403 запрещенной ошибки раньше. Я хочу знать, почему это работает

GeneratePresignedUrlRequest принимает аргумент метода http (и имеетsetMethod функция), это кажется непригодным для всего, кроме GET.

http://wiki.nercomp.org/wiki/images/0/05/AmazonWebServices.pdf гласит: «Практика подписания запроса и передачи его третьему лицу для исполнения подходит только для простых GET-запросов объекта». Возможно установка другого метода может быть использована для чего-то, но, видимо, не это.

Поэтому вместо этого я должен был следовать инструкциям здесь:

http://aws.amazon.com/articles/1434?_encoding=UTF8&jiveRedirect=1

Это более сложно, потому что клиент должен публиковать полную форму, а не просто использовать URL-адрес, а также означает, что вся эта информация о сообщении должна передаваться клиенту отдельно, но, похоже, это работает.

 Steffen Opel31 янв. 2012 г., 17:44
Интересно, я не знал об этом (прежнем?) Ограничении - оно было удалено изтекущая документация по этому вопросу, хотя и, например,PUT на S3: проблема иллюстрирует явно работающий пример использованияPUT а также (несмотря на обсуждаемые там побочные проблемы).
 Pochi18 июл. 2012 г., 08:48
@ SteffenOpel Я полагаю, что проблема заключается в создании запроса пут при использовании Java SDK, более конкретно в подписи. Возвращенный этим URL-адрес может использоваться для запроса к серверу, но вы получите запрещенную ошибку вместе с некоторой строкой для подписи байтов (запрос). Если вы подпишите эту строку чем-либо еще, подпись будет отличаться. Вы даже можете обменять эту новую подпись на подпись в запросе, и запрос будет работать. может быть, это ошибка Java
 Bjorn Roche31 янв. 2012 г., 21:28
Спасибо за продолжение.
 Steffen Opel31 янв. 2012 г., 19:29
Я только что подтвердил, что использование PUT действительно работает очень хорошо, как и ожидалось, ge, предварительно назначив предварительно подписанный URL через C # и затем загрузив его черезлокон - Я опубликую более подробную информацию в качестве ответа после перерыва.

Ваш ответ на вопрос