LoginSignup
5
3

More than 5 years have passed since last update.

React Native にて FormData 形式でファイルをアップロードしようとして詰まった話

Last updated at Posted at 2019-04-18

はじめに

自分が携わるプロジェクトでは、数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 の導入・リファクタリングはそのうち取り掛かります。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3