これは確かに少し不可解です。AWS SDK for 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
背景/分析
Amazon S3への PUT (および DELETE と HEAD) リクエストの事前署名付き URL は、このサイトの関連する質問で少なくとも証明されているわけではなく、原則として機能することが知られています (たとえば、事前署名付きを使用して curl で s3 にアップロードするための私の回答を参照してください)。 URL (取得 403) )。
促進されたQuery String Request Authentication Alternativeは、クエリ文字列リクエスト認証方法を示す次の疑似文法を使用するように文書化されています。
StringToSign = HTTP-VERB + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Expires + "\n" +
CanonicalizedAmzHeaders +
CanonicalizedResource;
これにはヘッダーが含まれてContent-Type
おり、(既に発見したように) いくつかの文書化されたケースではこれが欠落していました。たとえば、AWS チームが PUT request を使用して GetPreSignedURL に応答し、追加されると事前に署名された URL が機能することを確認してください。
これは、 AWS SDK for .NETを使用して簡単に実現できます。これは、まさにそれを行う便利なメソッドGetPreSignedUrlRequest.WithContentTypeを提供します。
このリクエストの ContentType プロパティを設定します。このプロパティのデフォルトは「binary/octet-stream」ですが、他に何か必要な場合は、このプロパティを設定できます。
したがって、それぞれのサンプルUpload an Object Using Pre-Signed URL - AWS SDK for .NETを次のように拡張すると、期待どおりに (つまり、試みたとおりに) curlを介してアップロードできる、コンテンツ タイプを含む動作する事前署名付き URL が生成されます。
// ...
GetPreSignedUrlRequest request = new GetPreSignedUrlRequest();
// ...
request.WithContentType("image/jpg");
// ...
ここで、意味的に同一のサンプルUpload an Object Using Pre-Signed URL - AWS SDK for Javaを同様の方法で拡張したいと考えていますが、(すでにお気づきのとおり)、これを実現するための専用の方法はありません。ただし、これは便利なメソッドが不足している可能性があり、最終的にaddRequestParameter ()またはsetResponseHeaders( ) を介して達成できる可能性があります。
// ...
request.setExpiration( new Date( System.currentTimeMillis() + (120 * 60 * 1000) ));
request.addRequestParameter("content-type", "image/jpg");
return client.generatePresignedUrl( request ).toString();
// ...
ただし、両方のメソッドのドキュメントは他の目的を示唆しており、実際には機能しません。つまり、どのコンテンツ タイプがそのように設定されていても (存在する場合)、常に同じ署名を生成します。
SDK をさらにデバッグすると、上記の疑似文法に従ってクエリ文字列認証を計算する意味的に類似したコア メソッドが両方とも提供されていることがわかります。.NET の場合は buildSigningString () 、Java の場合はmakeS3CanonicalString()を参照してください。
しかし、Java バージョンのAdd all Interested Headers to a list, then sort themのそれぞれのコードは、「Interesting」が Content-MD5、Content-Type、Date、および x-amz- として定義されているため、実際には実行されません。これらのヘッダーを何らかの形で提供する方法は実際にはありません。これはクラスDefaultRequestでのみ使用でき、前者を初期化するために使用されるクラスGeneratePresignedUrlRequestでは使用できません。これは、署名を計算するための入力として使用されます。保護されたメソッドcreateRequest()を参照してください。
興味深いことに、.NET と Java でクエリ文字列認証を計算する 2 つの方法は、コール スタック上のヘッダーソースとパラメーターソースのほぼ逆の組み合わせから入力を構成します。これは、Java バグの原因を示唆している可能性がありますが、明らかに、それは解読するのが難しいだけかもしれません。つまり、内部アーキテクチャはもちろん大幅に異なる可能性があります。
暫定的な結論
これには 2 つの角度があります。
- AWS SDK for Java には、コンテンツ タイプを設定するための便利な方法が明らかに欠けています。これは比較的まれかもしれませんが、それに応じて他の AWS SDK で説明されている明らかなユース ケースです。AWS 関連のバックエンド サービスで広く使用されていることを考えると、これは驚くべきことです。 .
- とにかく、たとえば.NETバージョンと比較して、クエリ文字列リクエスト認証の実装方法に何か怪しいものがあるようです-これもまた、コア機能であることを考えると驚くべきことですが、これはまだS3モデル内にあります/名前空間であるため、上記のそれぞれの使用例でのみ必要になる場合があります。
結論として、これを解決する唯一の合理的な方法は更新された SDK であるため、バグ レポートが必要です。明らかに、SDK 機能を複製/拡張して、この特殊なケースを個別に説明することもできます (理想的には、送信できる方法で)。aws-sdk-for-java プロジェクトのプル リクエスト) が、互換性があり保守可能な方法でこれを適切に行うのは少し難しいように思われるため、SDK メンテナー自身が行うのが最善の方法である可能性があります。