はじめに
自分が携わるプロジェクトでは、数100kB程度の軽い画像ファイルを通信する際に、 GraphQL multipart request で直接ファイルを転送しています。
ionic + cordova で動作している既存処理を React Native に移行する際に色々と試行錯誤しました。
かなりニッチなシチュエーションですが、同じ状況に悩む人の救いになれば、と備忘も兼ねて記事に残します。
ニッチなシチュエーション
- 動作環境:React Native
- react-native-signature-pad で生成した署名データ
- 手動で作成した HTTP Header / Body
content-type: 'multipart/form-data'
- 仕様:GraphQL multipart request
既存のロジック(ionic)
現在動いているシステムでは、http header / body を自前で構成してサーバサイドにポストする処理が実装されていました。
参考までに、下記のような感じです。(ボディ部の定義のみ記載しています。)
file: null
は意図的です。詳細はコチラをご覧ください。
const body = new FormData();
body.append(
'operations',
JSON.stringify({
operationName: 'UploadFileMutation',
variables: { file: null },
query: `mutation UploadFileMutation($file: Upload!) {
uploadFile(file: $file) {
success
}
}`,
}),
);
body.append('map', JSON.stringify({ file: ['variables.file'] }));
body.append('file', this._fileBlob);
ハマったこと(その1)
結論を先に言うと、今回 axios の利用は断念しました。
その理由は、こちらの issue にあります。
生成したデータを axios でサーバ側に post していましたが、リクエストを見ても content-type
がセットされていませんでした。
ボディ部のデータが悪いのかな?と思い、色々とトライしてみてもうまくいかず…。
上述の issue では、似たような状況に陥っている人が見受けられました。(React Native 特有の問題のよう)
結局、解決策は見つけられなかったのであるコメントを参考にして、XMLHttpRequest で通信することにしました。
その結果、正しく content-type: 'multipart/form-data'
がセットされるようになりました。
ハマったこと(その2)
その1で、content-type
は正しくセットされるようになりましたが、ファイルのアップロードはまだうまく処理されていませんでした。
今回のケースでは、既存の処理に倣い、ファイルは Blob
で通信を試みていました。
試行錯誤する中で、気になるコメントを見つけました。
true gets stringified when sent to the server, which is interesting.
Boolean で処理したはずの true
という値がサーバ側では String の 'true'
になっている、という内容でした。
このコメントは axios のケースですが、axios は xhr adapter を利用しているようなので、もしかしたら xhr にも当てはまるのでは?と仮定し、 Blob
での処理を断念しました。
代わりに、apollo-upload-client にある、 ReactNativeFile クラスを利用しました。
const file = new ReactNativeFile({
uri: values.signatureDataUrl,
type: mimeType,
name: 'blob',
});
Wiresharkを使ったリクエストの確認やサーバ側の受信データの確認をサボってしまったので、推測が入ってますが、ともあれこれでバックエンドに正しくファイルをアップロードできました。
おわりに
その2で導入している、apollo-upload-client を使えばもっと簡単じゃないの?って思った方もいるかもしれません。
おっしゃる通りです。
いきなり apollo-upload-client を導入しようとして色々とエラーが発生したので、ステップバイステップで既存処理のコピーをやろうとしてまたハマるというお間抜けな展開でした。
とりあえず React Native 上で既存処理と同等の処理は動作したので、apollo-upload-client の導入・リファクタリングはそのうち取り掛かります。