🏇

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ファイルをダウンロードできる必要があります。

修正前のPythonコード
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-Typeapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet が設定されます。

この状態で署名済みURLを作成し、ダウンロードしてみます。

ダウンロードされず、Excelファイルが開かれました。

解決策

これをファイルを開くのではなく、ダウンロードを強制するには Content-Type をバイナリファイル (binary/octet-stream) に設定することでブラウザ側にダウンロードを実行させます。

またS3オブジェクトのコピーでは、デフォルトで元のファイルのメタデータが引き継がれます。そのためMetadataDirectiveREPLACEにすることで、元のファイルの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' #これではない
    }
)

参考

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/copy_object.html

アイレット株式会社

Discussion