S3のファイルをダウンロードするとき、期限付きのURLやダウンロードファイルの名前を変更したりできるpre-signed URLという機能があります。
AWS系のライブラリを使えば簡単なのですが(参考)、使うと必要ないものが沢山ついてきてdeploy作業とかが重くなるのいやなので、シンプルにPure Pythonで書いたのでそのコードをココで公開しておきます。
※シンプルなコードなのでPythonじゃなくても真似すればどんな言語でも動きそう。
このURLはGETリクエストのみに対応しています。
POSTに対応したい場合はc_string文字列の中のGETをPOSTとかにすると動くかもです(未確認)。
access_keyとsecret_keyはご自身のAWSのモノをご利用下さい。
c_stringはリクエストの内容の文字列で、これをSHA1でAWSのSecret Keyで暗号化した文字列をhmacでハッシュ化してHTTPリクエストの文字列に耐えられるようbase64エンコードと特殊文字のエスケープ(urllib.quote)により署名とします。
これらのエンコード処理を行わないと、SignatureにはURIパラメータに適さない文字列を作成することがあり、AWS側でそれをデコードしてしまいSignatureが一致しない「SignatureDoesNotMatch」というエラーが発生することがあります。
あとはリクエストURLを構築するだけです。
S3に格納されたファイルのベースのURLに署名や有効期限(本スニペットではURL作成時から1日)、AWSのAccess Keyを付け、最後にダウンロードファイル名の指定します。
これでOKです。
c_stringがちょっとクセモノですが、他の署名については一般的なOAuth2.0などの方法と同じでシンプルで分かりやすかったです。
使い方は簡単で例えば以下のような使い方です。
bucketにS3のバケット名を指定。
pathはダウンロードファイルのkey名です。
output_filenameはダウンロードファイル名を指定します。
この3つのパラメータから、期限付きかつダウンロードするファイル名を変更したURLを生成できます。
たった十数行のコードで巨大なbotoを使わずに済みました。
蛇足ですが、
ファイル名を変更する必要がない場合は全てのresponse-content-dispositionのパラメータを外せばOKです。
response-content-dispositionを外さず元のファイル名を指定しておけば済む話ですが(汗)
AWS系のライブラリを使えば簡単なのですが(参考)、使うと必要ないものが沢山ついてきてdeploy作業とかが重くなるのいやなので、シンプルにPure Pythonで書いたのでそのコードをココで公開しておきます。
※シンプルなコードなのでPythonじゃなくても真似すればどんな言語でも動きそう。
def generate_url_s3(bucket, path, output_filename): import time import hmac import hashlib import base64 import urllib access_key = 'YOUR_AWS_ACCESS_KEY' secret_key = 'YOUR_AWS_SECRET_KEY' expire_in = 86400 # 1 day (60 x 60 x 24) expire = int(time.time() + expire_in) c_string = 'GET\n\n\n%d\n/%s/%s?response-content-disposition=attachment; filename="%s"' % (expire, bucket, path, output_filename) sign0 = base64.b64encode(hmac.new(secret_key, c_string, hashlib.sha1).digest()) sign = urllib.quote(sign0, '') q = 'Signature=%s&Expires=%d&AWSAccessKeyId=%s' % (sign, expire, access_key) extra = 'response-content-disposition=%s' % urllib.quote('attachment; filename="%s"' % output_filename) url = "http://%s.s3.amazonaws.com/%s?%s&%s" % (bucket, path, q, extra) return url
このURLはGETリクエストのみに対応しています。
POSTに対応したい場合はc_string文字列の中のGETをPOSTとかにすると動くかもです(未確認)。
access_keyとsecret_keyはご自身のAWSのモノをご利用下さい。
c_stringはリクエストの内容の文字列で、これをSHA1でAWSのSecret Keyで暗号化した文字列をhmacでハッシュ化してHTTPリクエストの文字列に耐えられるようbase64エンコードと特殊文字のエスケープ(urllib.quote)により署名とします。
これらのエンコード処理を行わないと、SignatureにはURIパラメータに適さない文字列を作成することがあり、AWS側でそれをデコードしてしまいSignatureが一致しない「SignatureDoesNotMatch」というエラーが発生することがあります。
あとはリクエストURLを構築するだけです。
S3に格納されたファイルのベースのURLに署名や有効期限(本スニペットではURL作成時から1日)、AWSのAccess Keyを付け、最後にダウンロードファイル名の指定します。
これでOKです。
c_stringがちょっとクセモノですが、他の署名については一般的なOAuth2.0などの方法と同じでシンプルで分かりやすかったです。
使い方は簡単で例えば以下のような使い方です。
bucket = "mybucket" path = "tmp/myfile.zip" output_filename = "sample.zip" url = generate_url_s3(bucket, path, output_filename) print(url)
bucketにS3のバケット名を指定。
pathはダウンロードファイルのkey名です。
output_filenameはダウンロードファイル名を指定します。
この3つのパラメータから、期限付きかつダウンロードするファイル名を変更したURLを生成できます。
たった十数行のコードで巨大なbotoを使わずに済みました。
蛇足ですが、
ファイル名を変更する必要がない場合は全てのresponse-content-dispositionのパラメータを外せばOKです。
response-content-dispositionを外さず元のファイル名を指定しておけば済む話ですが(汗)