Ответ 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.