Подтвердить что ты не робот

PUT файл на S3 с назначенным URL-адресом

Я играю с назначенными URL-адресами Amazon S3 всю ночь, пытаясь поместить файл. Я генерирую назначенный URL-адрес в Java-коде.

    AWSCredentials credentials = new BasicAWSCredentials( accessKey, secretKey );
    client = new AmazonS3Client( credentials );
    GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest( bucketName, "myfilename", HttpMethod.PUT);
    request.setExpiration( new Date( System.currentTimeMillis() + (120 * 60 * 1000) ));
    return client.generatePresignedUrl( request ).toString();

Затем я хочу использовать сгенерированный, назначенный URL для PUT файла с помощью curl.

curl -v -H "content-type:image/jpg" -T mypicture.jpg https://mybucket.s3.amazonaws.com/myfilename?Expires=1334126943&AWSAccessKeyId=<accessKey>&Signature=<generatedSignature>

Я предположил, что, подобно GET, это будет работать на ведро, которое не является общедоступным (что точка назначенного, правильно?) Ну, я получил доступ к отказам при каждой попытке. Наконец, из-за разочарования я изменил разрешение ведра, чтобы позволить КАЖДОМУ писать. Разумеется, тогда работал назначенный URL. Я быстро удалил разрешение EVERYONE из ведра. Теперь у меня нет разрешения удалять элемент, который был загружен в мое ведро, по моему собственному предварительно подписанному URL. я см. Сейчас, что я, вероятно, должен был разместить заголовок x-amz-acl на том, что я загрузил. Я подозреваю, что я создам еще несколько объектов, способных к восстановлению, прежде чем я получу это.

Это приводит к нескольким вопросам:

  • Как я могу загрузить с помощью curl с помощью PUT и сгенерированного назначенного URL?
  • Как удалить загруженный файл и созданное ядро ​​для его проверки?

Конечная цель заключается в том, что мобильный телефон будет использовать этот назначенный URL для PUT изображений. Я пытаюсь сделать это в curl как доказательство концепции.

Обновление: Я задал вопрос о форумах amazon. Если ответ будет предоставлен, я отвечу здесь как ответ.

4b9b3361

Ответ 1

Это действительно немного озадачивает, я считаю, что это ошибка в AWS SDK для Java (см. ниже) - но сначала и, прежде всего, следующая команда curl выгрузит ваш файл как таковой (при условии, конечно, обновленный предварительно подписанный URL-адрес):

curl -v -T mypicture.jpg https://mybucket.s3.amazonaws.com/myfilename?Expires=1334126943&AWSAccessKeyId=<accessKey>&Signature=<generatedSignature>

То есть я исключил заголовок Content type, который дает application/octet-stream (или binary/octet-stream) в результате, что явно не желательно; таким образом, дальнейшее копание было порядка.

Фон/анализ

Подписанные URL-адреса для запросов PUT (и DELETE, а также HEAD) для Amazon S3, как известно, работают в принципе, а не (см., например, мой ответ на Загрузка в s3 с завиванием с использованием предварительно подписанного URL (получение 403)).

В облегченном Альтернативе альтернативного запроса запроса строки запроса документируется использование следующей псевдограмматики, которая иллюстрирует метод проверки запроса строки запроса:

StringToSign = HTTP-VERB + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Expires + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;    

Он включает заголовок Content-Type, и (как вы уже обнаружили) это был недостающий кусок в некоторых документальных случаях, см., например, ответ команды AWS на GetPreSignedURL с запросом PUT, с добавлением рабочего предварительно подписанного URL после добавления.

Этого легко достичь с помощью AWS SDK для .NET, который обеспечивает удобный метод GetPreSignedUrlRequest.WithContentType сделать именно это:

Устанавливает свойство ContentType для этого запроса. Это свойство по умолчанию на "binary/octet-stream", но если вам требуется что-то еще, вы можете установите это свойство.

Соответственно, расширение соответствующего образца Загрузка объекта с использованием предварительно подписанного URL - AWS SDK для .NET следующим образом дает рабочий пре- подписанный URL-адрес с типом контента, который может быть загружен с помощью завитка, как и ожидалось (т.е. точно так же, как вы пытались):

    // ...
    GetPreSignedUrlRequest request = new GetPreSignedUrlRequest();
    // ...
    request.WithContentType("image/jpg");
    // ...

Теперь хотелось бы расширить семантически идентичный образец Загрузить объект с использованием предварительно подписанного URL - AWS SDK для Java в аналогичном но, как вы уже выяснили, не существует специального метода для достижения этого. Это может быть просто недостатком метода удобства, хотя и может быть достигнуто с помощью addRequestParameter() или setResponseHeaders() в конечном итоге, например:

  // ...
  request.setExpiration( new Date( System.currentTimeMillis() + (120 * 60 * 1000) ));
  request.addRequestParameter("content-type", "image/jpg");
  return client.generatePresignedUrl( request ).toString();
  // ...

Однако в обеих документах метода предлагаются другие цели, и это не работает, т.е. они всегда дают идентичную подпись, независимо от того, какой тип содержимого установлен так (если есть).

Отладка в SDK показывает, что оба предоставляют семантически подобный основной метод для вычисления аутентификации строки запроса в соответствии с упомянутой выше псевдограммой, см. buildSigningString() для .NET и makeS3CanonicalString() для Java.

Но соответствующий код в версии Java добавляет все интересные заголовки в список, а затем сортирует их, где "Интересный" определяется как Content-MD5, Content-Type, Date и x-amz- никогда не выполняется в факт, потому что действительно нет способа предоставить эти заголовки каким-то образом, которые доступны только для класса DefaultRequest, а не класса GeneratePresignedUrlRequest, используемый для инициализации первого, который используется как вход для вычисления подписи, в свою очередь, см. защищенный метод createRequest().

Интересно/Примечательно, что два метода вычисления аутентификации строки запроса в .NET и Java составляют их входные данные из почти обратной комбинации источников заголовков и источников в стеке вызовов, которые могли бы намекнуть на причину Java ошибка, но, очевидно, это может быть просто трудно расшифровать, т.е. внутренняя архитектура может значительно различаться, конечно.

Предварительное заключение

Для этого есть два угла:

  • В AWS SDK для Java определенно отсутствует удобный метод для настройки типа контента, который может быть сравнительно редким, но тем не менее очевидным случаем использования, учитываемым в других SDK SDS соответственно - это удивительно, учитывая его широкое использование в AWS связанных с бэкэнд-услугами.
  • Несмотря на это, похоже, что что-то подозрительное в том, как реализована аутентификация запроса на запрос строки запроса по сравнению с версией .NET, например, снова это удивительно, учитывая, что это основная функциональность, однако это все еще находится в пределах S3/пространство имен и, следовательно, может потребоваться только для соответствующих случаев использования выше.

В заключение, единственным разумным способом решения этого вопроса будет обновленный SDK, поэтому отчет об ошибках в порядке - очевидно, можно было бы также дублировать/расширять функциональность SDK для учета этого особого случая отдельно (в идеале в некотором смысле позволяя отправить запрос на перенос для aws-sdk-for-java project), но получение этого права в совместимом и поддерживаемом режиме кажется быть немного сложным, поэтому, скорее всего, лучше всего сделать сами поддерживающие SDK.

Ответ 2

В эту проблему тоже входит. Мы уже отслеживаем, когда файл загружается на бэкэнд, поэтому наша работа заключалась в том, чтобы установить тип содержимого после того, как клиент загрузит файл с помощью приложения Rails с вызовом copy_from.