React NativeでiOSのランディングスクリーンに画像を設定する方法
という記事を書いた。
だいたい図にした。constrainsの設定の時だけ、対象のオブジェクトからviewへcontrol + click してドラッグというのがわかりにくいかも。
Presigned post to aws s3 from React Native
It took a longtime to figure out how to upload images to aws s3 using presigned post from React Native.
The key part is to use FormData to construct a payload and post it with POST
http method. PUT
ing FormData payload with pre-signed URL didn’t work for me at least.
Server Side
I use Ruby on Rails for server side here. Read Direct to S3 Image Uploads in Rails | Heroku Dev Center to fully understand the code below.
FYI: You don’t need to setup CORS settings in the article for native apps.
The security model for XMLHttpRequest is different than on web as there is no concept of CORS in native apps. - https://facebook.github.io/react-native/docs/network.html
Here is the code:
# In your controller def presigned_url options = { key: "uploads/photos/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read' } presigned_post = S3_BUCKET.presigned_post(options) # FIXME: http://stackoverflow.com/a/36941996/2930161 if presigned_post render plain: { presignedPost: { fields: presigned_post.fields, url: presigned_post.url } }.to_json, status: 200, content_type: 'application/json' else render plain: { error: 'No presigned urls.' }.to_json, status: 422, content_type: 'application/json' end end
React Native
// In your component. async _uploadToS3() { try { // implement your method to fetch a presigned post object. const presignedPost = await this._fetchPreSignedPost() let formData = new FormData(); Object.keys(presignedPost.fields).forEach((key) => { formData.append(key, presignedPost.fields[key]); }); formData.append('file', { // Provide an url of a target image. uri: this.image.uri, type: 'image/jpeg', name: 'image.jpg', }) const presignedUrl = presignedPost.url fetch(presignedUrl, { method: 'POST', body: formData, headers: { 'Content-Type': 'multipart/form-data'} }) } catch (error) { console.log(error) } }
This Answer on StackOverflow inspired me for the code, thanks:
Other Links
http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Bucket.html#presigned_post-instance_method
http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/PresignedPost.html
Upload an Object Using a Pre-Signed URL (AWS SDK for Ruby) - Amazon Simple Storage Service
react-native/XHRExampleFormData.js at master · facebook/react-native · GitHub
React Nativeのリリース間隔は月次
2017/1 から。以前は2週間に一度だった。月末に新しいバージョンがリリースされるので、月の初めには新しいバージョンが利用可能になっている。また、リリース時にはすでにFacebookのプロダクションで2週間ほど運用した状態であるだろうとのこと。
Relay: containerのinitialVariablesに値を渡す
追記: 2017/08/31 これはRelay Classicの時に書いたものです。
Introduction
非同期処理で何かごにょごにょした後、Relayにその値を使ってサーバーにリクエストを投げてほしかった。
やり方
Relay.createContainerを関数でラップする
雑な実装例:
componentDidMount() { this._asyncFunc() } async _asyncFunc() { try { let asyncVal = await someAsyncThings() this.setState({asyncVal}) } catch (error) { console.log(error) } } render () { let component = null if (this.state.asyncVal) { component = ( <Relay.RootContainer Component={createRelayComponent(this.state.asyncVal)} route={yourRoute} renderLoading={function() { return <div>Loading...</div>; }} /> ) } else { component = <div>Loading...</div>; } return component } function createRelayComponent(asyncVal) { return ( Relay.createContainer(Component, { initialVariables: { yourVar: asyncVal } fragments: { ... } }) ) }
参考リンク
感想
他のやり方があれば教えほしい
React Nativeの画像キャッシュ
追記 2017-11-27: 結局今はFastImageを使っている。GitHub - DylanVann/react-native-fast-image: 🚩 FastImage, performant React Native image component. 最高。
プロローグ
React Native使ってみた。Image
コンポーネント、毎回ネットワークから取得してるじゃん、これキャッシュしてくれないの?と思った。
調べた
[0.32] Image caching broken · Issue #9581 · facebook/react-native · GitHub
なんか壊れてんヨーという報告を発見。やっぱ壊れてんのかと思いつつ。 でもキャッシュしてもらえる人ともらえない人がいるみたい。日頃のおこない? ちなみに俺はキャッシュしてもらえてない。バージョンは0.38.0で。
なんでやねんと色々思っていると、画像サイズが大きいとダメだというコメントが目に入る。 画像サイズを小さくして試してみる。いけた。600KB -> 178kBぐらいに絞った。
どうやらReact Nativeは画像の取得にiOSに元からあるNSURLRequest
という仕組み使っていて、そのNSURLRequest
はNSURLCache
さらにはNSURLSession
とやらの仕様にしたがってキャッシュを行うらしい。
そしてこのNSURLSession
は指定した上限の、5%までのファイルサイズしかキャッシュしないみたい。
で、React Nativeはどれほど指定しているのかなーと思ったら
_decodedImageCache.totalCostLimit = 5 * 1024 * 1024; // 5MB
なるほど600KBはキャッシュされないですねと。てか上限小さすぎない?そしてそもそも
static const NSUInteger RCTMaxCachableDecodedImageSizeInBytes = 1048576; // 1MB
1MB以上のファイルはRN側でキャッシュしないように指定してるみたい。参照したのはここらへんhttps://github.com/facebook/react-native/blob/v0.38.0/Libraries/Image/RCTImageCache.m#L23
まぁただここらへんの挙動は変数名から推測した程度なので、自信はない。
TL;DR
画像サイズが大きいとダメみたい。250KB以下にした方が良さげ。
Image
コンポーネント、キャッシュしてくれる。シミュレーターでも動作が怪しいかもしれない。実機で動かしたらちゃんとキャッシュしてくれてた。
その他
[追記: 2017/3/30]
書き忘れていたけれど、画像をuriで指定して得るレスポンスのメタデータにcache-contolを含めていないとキャッシュしてくれない。 S3とかに上げるときは設定を確かめるのを忘れずに。
[追記: 2017/4/19]
Facebook内部ではオープンになってるコンポーネントとは別のImage, Cachingモジュールを使ってるみたい。 このIssueが閉じるまでは、Imageコンポーネントのキャッシュは安定しなさそう。自分の場合、3回ぐらいアプリ開くとキャッシュが消されるという挙動をしている。(0.43.3)
https://github.com/facebook/react-native/issues/9581#issuecomment-287834859
参考リンク
[0.32] Image caching broken · Issue #9581 · facebook/react-native · GitHub
https://github.com/facebook/react-native/blob/v0.38.0/Libraries/Image/RCTImageCache.m
ios - How to cache using NSURLSession and NSURLCache. Not working - Stack Overflow