Box APIを使ってファイルをアップロードする方法を調べたので記事を書きます。
経緯
仕事でBox APIを使っているのですが、ファイルアップロードがうまく行かずに詰まっていました。
帰宅後に調べて解決したので記事に書きます(スッキリしました)。
サンプルコード
GitHubに置いておきました。
https://github.com/flipfrog/box-api-upload-using-guzzlehttp
実装上のポイント
リファレンスにかかれている、cURLでの使い方をGuzzlehttpでどのように実装するかでした。
-F attributes='{"name":"Contract.pdf", "parent":{"id":"11446498"}}' \ -F file=@<FILE_NAME>
POSTMANを使うと、ほぼそのままリクエストパラメータを設定すればアップロードできたのですが、私は直感的にGuzzlehttpを使った実装ができませんでした。Guzzlehttpでは下記のように、multipartフォームを記述します。
'multipart' => [ [ 'name' => 'parent_id', 'contents' => 0, // 0: root folder ], [ 'name' => 'file', 'contents' => \GuzzleHttp\Psr7\Utils::tryFopen('./data/test.txt', 'rb'), 'filename' => 'my_test.txt', ], ],
最初のブロックの、name=parent_idの書き方は、Boxが提供するOpen API specification schemaから、openapi-generatorを使ってPHPのSDKを生成したときの src/Command/Content/File/UploadFile.php
ファイルに記載されていた方法を参考にしました。
2つ目のブロックのcontentsに、文字列やファイルが紐付かないstreamを指定したときには、同ブロックのfilenameを指定しないと400エラーになります。
詰まっていた理由
いくつかの理由でうまくいかずに困っていました。
アップロードするサーバーは他のエンドポイントとは別
リファレンス をよく見ていなかった自分が悪いですね。
APIを使ったファイルのアップロードに慣れていなかった
ほぼ、リファレンスに書かれている感じでPOSTMANを使うとファイルをアップロードできますが、Guzzlehttpを使って同じことをやろうとするとうまく行かない。というか、その方法が分からなかった。
下図はPOSTMANでのリクエストパラメータ設定です。
情報が少ない
ネットで検索すると、同じような質問が幾つかありましたが、どれも解決に至っていませんでした。 Box公式のSDKが無く、個人的に作成されたものをGitHubで見たのですが、使うGuzzlehttpのバージョンが4.xと古く、最新の7.xでは参考になりませんでした。
追記
記事を書いたあとに情報を見つけたのですが、multipartブロックを下記のように書けば直感的になるようです。
'multipart' => [ [ 'name' => 'attributes', 'contents' => json_encode([ 'name' => 'my_test.txt', 'parent' => ['id' => 0], ]), ], [ 'name' => 'file', 'contents' => \GuzzleHttp\Psr7\Utils::tryFopen('./data/test.txt', 'rb'), 'filename' => 'my_test.txt', // optional to specify file name on box ], ],
Boxに作成されるファイルのファイル名は、1つ目のブロックのJSON内のname属性が優先して採用されるようです。
出典: https://support.box.com/hc/ja/community/posts/5289184800019-API-upload-did-not-contain-a-file-part