boto3でS3上のExcelファイルを強制ダウンロードさせる方法
結論:boto3でS3上のExcelファイルを強制ダウンロードさせる方法
ContentType='binary/octet-stream'
とMetadataDirective='REPLACE'
を指定する。
s3_client.copy_object(
Bucket=destination_bucket,
CopySource={'Bucket': source_bucket, 'Key': source_key},
Key=destination_key,
ContentType='binary/octet-stream',
MetadataDirective='REPLACE'
)
前提
特定のS3バケットに既にExcelファイルが配置されており、そのファイルを AWS Lambda を使用して、別のバケットにコピーするユースケースを想定します。
以下のようなコードを AWS Lambda 側で実装します。
単純に元のバケットからコピー先のバケットに、元のままExcelファイルをコピーします。
このとき、コピー先ではExcelファイルをダウンロードできる必要があります。
import boto3
s3_client = boto3.client('s3')
def handler(event, context):
source_bucket = event['sourceBucket']
source_key = event['sourceKey']
destination_bucket = event['destinationBucket']
destination_key = event['destinationKey']
s3_client.copy_object(
Bucket=destination_bucket,
CopySource={'Bucket': source_bucket, 'Key': source_key},
Key=destination_key,
)
落とし穴
Excelファイルをダウンロードする際に、Chromeであればダウンロードされますが、Microsoft EdgeなどではダウンロードされずにExcelファイルがブラウザで開かれてしまいます。
普通に登録するとアップロードされたExcelファイルの Content-Type
は application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
が設定されます。
この状態で署名済みURLを作成し、ダウンロードしてみます。
ダウンロードされず、Excelファイルが開かれました。
解決策
これをファイルを開くのではなく、ダウンロードを強制するには Content-Type
をバイナリファイル (binary/octet-stream
) に設定することでブラウザ側にダウンロードを実行させます。
またS3オブジェクトのコピーでは、デフォルトで元のファイルのメタデータが引き継がれます。そのためMetadataDirective
をREPLACE
にすることで、元のファイルのContent-Type
を上書きして登録されます。
import boto3
s3_client = boto3.client('s3')
def handler(event, context):
source_bucket = event['sourceBucket']
source_key = event['sourceKey']
destination_bucket = event['destinationBucket']
destination_key = event['destinationKey']
s3_client.copy_object(
Bucket=destination_bucket,
CopySource={'Bucket': source_bucket, 'Key': source_key},
Key=destination_key,
ContentType='binary/octet-stream',
MetadataDirective='REPLACE'
)
ちなみにAWSコンソール上ではメタデータにContent-Type
があり、boto3のcopy_objectのドキュメントにはMetadata
パラメーターがありますが、Metadata
にContent-Typeを入れても以下のようにユーザー定義になります。
ユーザー定義の方は単純なラベルのため、ブラウザが判定することには使われませんので注意が必要です。
s3_client.copy_object(
Bucket=destination_bucket,
CopySource={'Bucket': source_bucket, 'Key': source_key},
Key=destination_key,
MetadataDirective='REPLACE',
Metadata={
'Content-Type': 'binary/octet-stream' #これではない
}
)
参考
Discussion