こんばんは。今週はReact Nativeにおける画像の扱いと戦っております。React Nativeのアプリからサーバサイドへ画像をアップロードしたいのですが、どうにもうまくいかない・・
トラブルシューティングしようにも、このあたりの技術に対する理解がなさすぎて辛くなってきたので、腰を据えて、勉強しようと思った次第です・・・
自分の抱えている問題を解決するために調べたことを残していっているだけなので、網羅的な解説にはなっていない点、ご容赦ください・・・
それでは参ります。
Contents
FormDataの概要
https://developer.mozilla.org/ja/docs/Web/API/FormData/Using_FormData_Objects
FormData
オブジェクトは、XMLHttpRequest
を使用して送信するためのキーと値のペアのセットを収集可能にします。本来はフォームデータの送信に使用することを想定していましたが、キーのついたデータを伝送するためにフォームとは独立して使用することもできます。伝送されるデータは、フォームのエンコードタイプがmultipart/form-data
に設定されている場合に、submit()
メソッドで送信する際に使用するデータと同じ形式です。
テキストデータだけでなく、FileやBlobをformdataとして送信することも可能。
data.append("myfile", myBlob, "filename.txt");
ちなみに、formdataで画像などのBlobを送る場合、上記のような送り方以外にも、こんな方式もあるみたい。
画像のBlob自体をセットする代わりに、画像のURI、ファイル名、MIMEタイプを設定を設定する方法のようです。私も、React Native+Expoで開発していて、どうにも上の方法では画像のアップロードが行えなかった(Android Emulatorの問題・・?)ので、他の方法を探していたところこちらの方法に辿り着きました。
let formData = new FormData();
// Assume "photo" is the name of the form field the server expects
formData.append('photo', { uri: localUri, name: filename, type });
https://stackoverflow.com/questions/42521679/how-can-i-upload-a-photo-with-expo
なんだこの送り方は・・MozillaのformData.append()の仕様書に戻ってみる。
https://developer.mozilla.org/ja/docs/Web/API/FormData/append
Syntaxのところに説明がありますが、formDataのappendは、以下の形式と定義されているよう。filenameは省略可能。疑問はここのvalueのところですね。以下の説明があった。
formData.append(name, value);
formData.append(name, value, filename);
The field’s value. This can be a
USVString
orBlob
(including subclasses such asFile
). If none of these are specified the value is converted to a string.
USVStringかBlobもOK、と書いていますね。が、この説明からも、先程の形式で送れる理由がわからない・・・。そのStack OverflowのDiscussionで、同じ疑問を投げかけている人がいますね・・
How did it work? Local URI shouldn't be accessible on the backend . right?
いろいろ調べてみましたが結局この原理は分からず・・・誰か知ってる人いたら教えてください・・
XMLHttpRequestとは
https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest
XMLHttpRequest
(XHR) オブジェクトは、サーバーと対話するために使用されます。ページ全体を更新する必要なしに、データを受け取ることができます。これでユーザーの作業を中断させることなく、ウェブページの一部を更新することができます。XMLHttpRequest
は AJAX プログラミングで頻繁に使用されます。
XMLHttpRequest
という名前ではあるものの、 XML だけでなくあらゆる種類のデータを受け取るために使用することができます。
ん、サーバと対話するために使用される?どういうことだ?上の図をみてもさっぱりなんのとこか分からない・・笑
Wikipediaには、もう少し別の説明と、歴史について記載がありました。マイクロソフトが利用し始めたのがきっかけだったようですね。
https://ja.wikipedia.org/wiki/XMLHttpRequest
XMLHttpRequest (XHR) は、JavaScriptなどのウェブブラウザ搭載のスクリプト言語でサーバとのHTTP通信を行うための、組み込みオブジェクト(API)である。
すでに読み込んだページからさらにHTTPリクエストを発することができ、ページ遷移することなしにデータを送受信できるAjaxの基幹技術である。
XMLHttpRequestを利用したWebアプリケーションは非常に多く存在し、例として、Google マップ、Facebookなどが挙げられる。
Wikipediaより
XMLHttpRequestはどういう場合に使われる?その答えはこちらに分かりやすく説明されておりました。
https://ja.javascript.info/xmlhttprequest
ここをみると、現在はfetch APIが主流というようなことが書いていました。ん、XMLHttpRequestの後継がFetchなの?ということで調べてみると、その点が分かりやすくまとめられたQiitaの記事を発見しました。ものすごいLGTM数です。そして分かりやすい。
https://qiita.com/uhyo/items/91649e260165b35fecd7
余談ですが、一体どれだけ技術に触れればこんな深く語れるようになるんだろう、と思って書いた方のGithubのContribution Historyを見てみると、ものすごいことになっていました(土日も関係なくずっとContributionされている・・・)
こういうレベルになると、ここまで語れるのですね・・・(絶望と尊敬)
一つ前の記事でも語られていましたが、現在においては、新しく開発する場合、fetchで実現できないことを実現したい場合を除いては、fetchを使う、という整理で良さそうだと理解しました。
話をformdataに戻すと、”XMLHttpRequest
を使用して送信するためのキーと値のペアのセットを収集可能にします。“と書いていますが、fetch APIを使っている人は、XHRをfetchと読み替えて解釈すれば良さそうです。
Multipart/Form-Dataとは
こちらの説明がとても分かりやすかったです。Boundaryという文字列で複数のデータを区切っているのですね。
https://www.yoheim.net/blog.php?q=20171201
一応、Mozillaの公式Docも。
https://developer.mozilla.org/ja/docs/Web/HTTP/Methods/POST
FormDataとしてデータを送信すると、multipart/form-dataとしてデータが送信されるのですね。
なるほど、ここまで事前知識を得ると、MozillaのFormDataの説明にあった以下の注意事項が理解できました。Fetch APIやXHRでformdataを送る際、明示的にContent-Typeを”multipart/form-data”として設定するような実装をしてしまうと、Boundary情報を埋め込めなくなってしまうからやめてね、ということなのですかね。
FormData を使用して、
XMLHttpRequest
またはFetch_API
を使用して、multipart/form-data
の Content-Type で POST リクエストを送信する場合 (Files や Blob をサーバーにアップロードする場合など)、リクエストのContent-Type
ヘッダーを明示的に設定しないでください。そうすると、ブラウザーがリクエスト本文のフォームフィールドの区切りに使用する境界の表現で Content-Type ヘッダーを設定することができなくなります。
まだもう少し足していくことになりそうですが、力尽きたので今日はここまで・・・
おしまい
コメントを残す