はじめに
作成したアプリの「呼出元でファイル添付ができない」が、「s3バケットに保持して利用させたい」とのことで、以下の仕組みを作成したことのメモ。
- s3バケットへの署名付きURLでアップロード
- cloudFrontでWebアップロードページの公開
- ※ httpリクエストのputが必要/リンククリックでは不可なため
構成
- s3/upload
- ファイルをアップロードするバケット
- プログラムでは、このバケットへの署名付きURLを作成する
- cloudFront
- 公開用URLを生成
- 「s3のアップロードページ」にリダイレクトさせる
- s3/contents
- 「index.html(WEBアップロードページ)」を保持するバケット
- cloudFrontのオリジンになる
しくみ
- http://******.cloudfront.net?s3_url=s3://upload/hoge.fuga URLを作成
- http://******.cloudfront.net
- cloudFrontのデフォルトURL
- s3/contentのindex.html(公開用URLのアップロードページ)にリダイレクトする
- ?s3_url=s3://upload/hoge.fuga
- 署名付きURLをパラメータとして保持
- 「公開用URLのアップロードページ」表示時に保持し、送信時に利用する
- http://******.cloudfront.net
AWSサービスの設定内容/注意点
s3/upload
バケットポリシー
Cross-Origin Resource Sharing (CORS)
s3/contents
バケットポリシー
CORS は設定不要
cloudFront
一般
オリジン
ビヘイビア
index.html(公開用URLのアップロードページ)
const binaryData // <- ページに渡したファイルのバイナリデータ
const xhr = new XMLHttpRequest();
xhr.open('PUT', presignedUrl, true); // <- 署名付きURLへのPUTをリクエスト送信対象とする
xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream');
xhr.send(binaryData); // <-アップロードページで保持したファイルを送信
実装(python)
- generate_presigned_url にて署名付きURLを作成できる
config = Config(signature_version='s3v4',
s3={'addressing_style': 'virtual'} # <- 仮想ホスト形式を強制
)
s3_client = boto3.client("s3", config=config)
url = s3_client.generate_presigned_url(
"put_object",
Params={"Bucket": bucket, # <- バケット名
"Key": key }, # <- バケット内の階層(putなのでファイルまで指定)
ExpiresIn=expire_seconds, # <- 署名付きURLの失効期限/秒
HttpMethod='PUT' # <- HTTPメソッドを明示
)
注意点
署名付きURLに「エンコード/デコード」が必要
署名付きURLはトークンや暗号化情報を保持する。
パラメータの欠落を防止するため「プログラムでエンコード/webページでデコード」が必要
-
エンコード (python)
base64.urlsafe_b64encode(presigned_s3_upload_url.encode()).decode()
-
デコード (html)
atob(encodedUrl.replace(/-/g, '+').replace(/_/g, '/'));
cloudFrontのキャッシュ削除
cloudFront は「オリジンを公開」している。
そのため、s3のindex.htmlを変更した時にキャッシュ削除が必要