Boxの分割アップロードAPI(chunked upload API)を使い、20MB以上のファイルをアップロードする方法を調べたときのサンプルコードが手元に残っていたので、メモを兼ねて記事に書きます。
経緯
仕事でBox APIを使い、比較的大きなファイルをアップロードする必要があったので、調査した結果を思い出しながらコードに落としました。 以前、Guzzlehttpでフォームを使ってファイルをアップロードをする方法を記事に書きましたが、それの分割アップロード版です。
サンプルコード
GitHubに置いておきました。
https://github.com/flipfrog/box-api-chunked-upload-test
実装上のポイント
Box APIでは、下記の手順でファイルをアップロードします。
- アップロードセッションを作成します
- ファイルを分割して、分割されたチャンク(chunk)毎にBoxのサーバーに送信します
- それぞれのチャンクは、並列に送信できます
- アップロードセッションをコミットします
- アップロードセッションを削除します
チャンクを送信するときの並列度はOSやメモリーの制約内であれば任意の値を取れそうですが、実験ではある程度の値を超えると性能が頭打ちになりました。ネットワークやBox APIサーバーの混み具合にも依存すると思うので状況に合わせて最適値を決める必要がありそうです。今回のサンプルコードでは、並列度5で定数を設定していますが、あくまでも一例です。
また、チャンクドアップロードは、サイズが20MB以上のファイルを対象にしているので、それ未満のファイルを送信しようとするとエラーになります。ファイルサイズによって、前回記事でのフォームを使ったアップロードと今回のチャンクドアップロードを切り替える必要があります。
ハマったポイント
実装時に少しハマったのが、アップロードセッションをコミットするときにPOSTのペイロードに格納するchunkの一覧をソートせずに送信して400系のエラーを返されたことでした。
今回はGuzzlehttpの並列送信機能を利用しました。 並列度の範囲内ですが、各チャンクは並列に処理されるため、それぞれの送信時間にはばらつきが発生します。その結果、単純に返ってきたpromiseの情報を配列に追加するだけでは、チャンク毎のファイル内でのオフセット位置の昇順でなくなってしまいます。 そのため、全てのチャンクを送信した後にコミット情報をチャンクのファイル内オフセットでソートする必要がありました。
サンプルコードで注意するところ
GitHubリポジトリにあるサンプルプログラムtest.php
は、ファイルを新規にアップロードする処理になっています。
既存ファイルを更新する場合は、そのファイルのIDを指定する必要があり、ファイルIDを指定しないと、409エラーが発生します。
このサンプルでは、ファイルIDを取得する処理は含まれていないので、各自でファイルIDを保持したり、仮想的なパスを基にファイルIDを取得する必要があります。
アップロードの動作を確認するだけであれば、下記の2箇所を修正することで、既存ファイルを更新することができます。
サンプルコードの動かしかた
前回のBox APIの記事に使ったコードと同じ構成にしてあります。
composer install
を実行します。- Boxのデベロッパーコンソール > アプリを選択 > Configurationタブ > Developer Tokenからトークンを取得して、サンプルコードリポジトリのconfig/box.phpに設定します。
- README.mdにも書いていますが、
config/box.php.example
をconfig/box.php
にコピーして下さい。
- README.mdにも書いていますが、
今回は20MB超えの大きめのファイルを送信するために、テストファイルをリポジトリに置いていません。その代わりに、C言語で書いた簡単なプログラムで任意のMB数のテストファイルを作成できるようにしてあります。
- こちらもREADME.mdに書いていますが、
make
を実行することにより、テストファイル作成プログラムがコンパイルされた後に、テストファイルをdata/test.dat
として作成します。Makefile内の21というMB単位のファイルサイズを変更すれば、任意のサイズのファイルを作成できます。Makefile内の数値を変更する、data/test.datファイルを消す、makeを実行する手順で希望のサイズのファイルを作成できます。
- こちらもREADME.mdに書いていますが、
準備ができたら、
php test.php
で実行します。
コードについて
Box APIの使い方についてフォーカスしたので、特にクラス化などは行っていません。