こんばんはD3vel0pperです。
今日は表題の通りの記事です。
何が起こったか
結論から書くと、 chromeの不具合(仕様?)により、s3にあるファイルをダウンロードする際に、一手間かけないといけなくなりました。
具体的には、ファイルを putFileAs()
を用いてs3上にアップロードする際、第4引数にメタデータとして ['ContentType' => 'binary/octet-stream']
を指定する必要があります。
環境
Google Chrome 69.??
laravel 5.5
flysystem-aws-s3-v3(s3のドライバ) ?? ← 現在確認中です
経緯
最近業務でlaravelを触っており、またデータベースやバックエンドにawsのdynamodbやs3を採用したプロジェクトを担当しております。
その中で、laravel上で作成し、s3にアップロードしたcsvファイルをlaravelにて構築したwebアプリケーション上からダウンロードする処理がありました。
その際、以下のようなコードを書いておりました。
<a href="path_to_s3_file" download>hogefilename.csv</a>
upload処理を担当するController部分
Storage::disk('s3')
->putFileAs(
$path,
new File($local_path_to_file),
$filename,
'public'
);
この状態で、アップロードを行い、chromeに表示されたリンクをクリックしてダウンロードしようとすると、真っ白なページに飛ぶだけでダウンロードが始まりませんでした。
よくあるミスとして、awsのcledentialなどが間違っているパターンや、ファイルがそもそもアップロードされていないなどをチェックしましたが、どれも問題ありませんでした。
また、s3へのurlが間違っているかと思い、リンクurlとs3の管理画面から該当のファイルのurlを確認しましたが、こちらも問題なし…
と、上司と色々調べていたところ、アップロードされたファイルのメタ情報に違いがあるという指摘が。
確かに見てみると他のダウンロードできる(s3へ管理画面からアップロードしたもの)には Content-type: binary/octet-stream
が、一方ダウンロードできないものについては Content-Type: text/csv
が付与されていました。
そこで、管理画面上からこのメタデータの値を Content-type: binary/octet-stream
へと変更したところ、正常にダウンロードできるようになりました。
laravelにてどうやってメタ情報をセットするか
ここまでくると、どうやってs3のメタ情報を指定するのか、と言うことを調べることになりました。
そして、結果的に以下のサイトを見つけました。
これによると、s3へのアップロード処理として使える putFileAs()
メソッドは、公式サイトのドキュメントでは 'public'
を指定することで公開状態にできる、と言う説明に止まっていますが、どうやら メタ情報を連想配列にして渡すことができる とのこと。
参考までに渡せるキーは以下が代表例です。
'CacheControl'
'Expires'
'StorageClass'
'ServerSideEncryption'
'Metadata'
'ACL'
'ContentType'
'ContentDisposition'
'ContentLanguage'
'ContentEncoding'
'visibility'
この中に、今回探していた ContentType
がありますので、これをキーとしてControllerを以下の処理へと書き換えました。
Storage::disk('s3')
->putFileAs(
$path,
new File($local_path_to_file),
$filename,
[
'visibility' => 'public',
'ContentType' => 'binary/octet-stream'
]
);
結果
この処理へと変更したのちにアップロードしたファイルは、きちんとダウンロードされるようになりました。
しかし、公式ドキュメントに書いておいてほしいと言うのは贅沢なんだろうか。。。
ちなみに、私は「よっしゃわかった!headerの書き方と同じだろ!」と思って ['Content-Type' => 'binary/octet-stream']
とか書いて30分ぐらい潰しましたので、みなさん同じ轍は踏まないでくださいね。。。
以上です。ご指摘等あればどしどし送ってください。