- はじめに
- Guzzleによるファイルアップロード方法1(Request Optionsにmultipartを指定)
- Guzzleによるファイルアップロード方法2(Request Optionsにheadersを指定)
- まとめ
- 仲間を募集しております
はじめに
こんにちは!株式会社デザインワン・ジャパンで口コミサービス エキテンのリニューアルを担当しているサービス開発部の鈴木セシル(@suzuki_cecil_)です。
弊社では現在、15年間運用され続けている口コミサービス エキテン のリニューアルプロジェクトに取り組んでおります。
リニューアルプロジェクトではHTTPクライアントに Guzzle を利用しているのですが、今回、Guzzleによるファイルのアップロードで少々苦戦することとなったためその時の内容を記事にまとめたいと思います。
Guzzleによるファイルアップロード方法1(Request Optionsにmultipartを指定)
POSTやPUTでファイルをアップロードするためには、Content-Typeをmultipart/form-dataにする必要があります。GuzzleでContent-Typeをmultipart/form-dataにする方法としては一般に、以下のサンプルコード1のようにRequest Optionsにmultipartを指定する方法が知られているかなと思います。
サンプルコード1
<?php use GuzzleHttp\Client; class FileStorage { private Client $client; public function __construct(private Client $client) { } public function upload(UploadedFile $uploadedFile) { $this->client->put("https://example.jp/test_1.jpeg", [ 'multipart' => [ [ 'name' => '', 'contents' => fopen($uploadedFile->openFile()->getRealPath(), "r"), ], ] ] ); } }
GuzzleではRequest Optionsをコンストラクタインジェクションもしくはメソッドインジェクションすることで柔軟にリクエストをカスタマイズすることが可能です。
サンプルコード1ではRequest Optionsにmultipartを指定することで、multipart/form-dataを送信するようにカスタマイズしてあります。
ファイルアップロード処理の実装が完了したと思い、サンプルコード1を実行して動作確認したところ、ファイルのアップロード先に test_1.jpeg
が生成されていたのですが、生成されたファイルが壊れていました。
ファイルが壊れているということで、悪い予感がしたため test_1.jpeg
のバイナリデータを確認したところ、やはりヘッダ情報がバイナリデータに含まれていました。
Content-Disposition: form-data; name="file"; filename="phpMnWyGV"^M Content-Length: 32643^M
そのため test_1.jpeg
がjpgのフォーマットに則っておらずファイルが壊れていると認識されていたのです。
通常であれば方法1として記述した、Request Optionsにmultipartを指定する方法でファイルのアップロードを行うことは可能であるはずなのですが、今回私が試したところ何故かファイルのバイナリデータにヘッダ情報が含まれてしまいました(今回、ファイルが壊れた理由は判明できていないです。)
そこで後述するRequest Optionsにheadersを指定する方法でmultipart/form-dataを送信し、ファイルのアップロードを試みてみました。
Guzzleによるファイルアップロード方法2(Request Optionsにheadersを指定)
Request Optionsにmultipartを指定する以外の方法で、multipart/form-dataを送信できないかを確認するために公式ドキュメントのRequest Optionsのページを確認したところ headersの項目を見つけました。
公式ドキュメントによるとRequest Optionsにmultipartを指定するのではなくheadersとしてリクエストヘッダを直接定義することが可能なようでした。Request Optionsにheadersを指定するようにしたのがサンプルコード2です。
サンプルコード2
<?php use GuzzleHttp\Client; class FileStorage { private Client $client; public function __construct(private Client $client) { } public function upload(UploadedFile $uploadedFile) { $this->client->put("https://example.jp/test_2.jpeg", [ 'body' => fopen($uploadedFile->openFile()->getRealPath(), "r"), 'headers' => ['Content-Type' => 'multipart/form-data'], ] ); } }
サンプルコード2を実行してみたところファイルのアップロード先に test_2.jpeg
が作られている、またバイナリデータにheaderが含まれていないことが確認できました。
まとめ
公式ドキュメントのRequest Optionsのページのmultipartには以下のように記載があります。
Summary
Sets the body of the request to a multipart/form-data form.
リクエストのボディを multipart/form-data
形式で設定します、とあるためサンプルコード1とサンプルコード2は同義だと思うのですが、サンプルコード1ではファイルのバイナリデータにヘッダ情報が含まれ、サンプルコード2ではファイルのバイナリデータにヘッダ情報が含まれないという結果となりました。
今回のケースでは方法1として記述したRequest Optionsにmultipartを指定する方法だとファイルのバイナリデータにヘッダ情報が含まれてしまったため、方法2として記述したRequest Optionsにheadersを指定する方法で、リクエストのボディを multipart/form-data
形式で設定する方が安全であると考えます。
仲間を募集しております
募集中の職種については以下を御覧ください。