こんにちは,ぽちです.
来週7/12のReact Native Meetup#6でLTします.
ネタはたぶんExpo関連の何かをゆるっと話します.
react-native-meetup.connpass.com
本題です.
React Nativeでインターネット上の(Assetじゃない)画像ファイルをTwitterなどにシェアをしたい時ことが有ったのですが,その時にちょっとハマった知見を共有します.
困ったこと
WebサービスのAPIを叩くと,画像のリソースは基本的にURLで返されます.
React Native上表示させるのであれば,返されたURLをImageコンポーネントのsourceアトリビュートに{uri: xxxx}
という形で突っ込めばいいのですが,それを何処かに投稿するとなるとURLのままだと困るケースがあります.
Node.jsだと単純に画像のURLにGETしてpipeでStreamを繋いで投稿先にPOSTするとかが使えるかもしれませんが,React Nativeだと同等の手段がなく,困りました.
ShareコンポーネントはURLが受けれる
今回必要になったのは,Twitterへのシェアだったので,
Share コンポーネントを使えばURLのままでも行けるのでは?と思ったのですが,クライアントによってはいい感じで表示してくれなかったため,data-uriの形にして画像としてShareコンポネントに突っ込みたいからでした.
解決法その1: Expo.takeSnapshotAsync
まず初めに思いついたのは,Expoのモジュールにある画面のスナップショットを撮るメソッドtakeSnapshopAsync
を使って,Imageコンポーネントで描画し,そのImageコンポーネントのスナップショットを撮るという手法でした.
<Image source={{ uri: this.state.thumbnail }} resizeMode="contain" style={styles.thumbnail} ref="thumbnail" />
という形で,得られた画像のurlをthis.state.thumbnail
などに入れておき,それをImageコンポーネントで描画します.
takeSnapshopAsync
で指定するために,ref
アトリビュートを設定しておきます.
share() { Expo.takeSnapShotAsync(this.refs.thumbnail, { format: 'jpg', quality: 0.9, result: 'data-uri', }) .then((image)=>{ Share.share({ message: 'message', url: img, title: 'Share' }, { dialogTitle: 'Share', excludedActivityTypes: [], tintColor: 'green' }); }); }
このようにtakeSnapShotAsync
のresult
プロパティにdata-uri
を指定して画像を取得してShareコンポーネントのurlプロパティに設定すればTwitterなどのアプリに画像をシェアすることが出来ます.
しかし,実際にシェアされた画像を見ると・・・
このように白帯が入ってしまっています.
これはImageコンポーネントのスタイルにbackgroundColor: 'transparent'
を指定しても同様です.
自分はこれがどうしても気に食わなかったので別の方法を探しました.
解決法その2: buffer
次は,bufferというnpmモジュールを使いました.
GitHub - feross/buffer: The buffer module from node.js, for the browser.
これは,node.jsの標準で用意されているBufferをブラウザにも持ってくるという意図で作られているモジュールです.
これを使って,画像のarraybufferを取得しdata-uriに変換して渡してやろうと考えました.
import axios from 'axios'; import { Buffer } from 'buffer/'; axios .get(this.state.thumbnail, { responseType: 'arraybuffer' }) .then(response => { const image = Buffer.from(response.data).toString('base64'); Share.share({ message: `message`, url: `data:${response.headers['content-type'].toLowerCase()};base64,${image}`, title: 'Share' }, { dialogTitle: 'Share', excludedActivityTypes: [], tintColor: 'green' }); });
buffer/
となっているのはTypoではなく,そう指定するようにドキュメントに書かれています.
画像のarraybufferの取得は GitHub - mzabriskie/axios: Promise based HTTP client for the browser and node.js を使って,responseType
にaraybuffer
を指定してGETすることで取得しました.
request.js
とかsuperagent
とかfetch
でも出来るような気もします.
得られたarraybufferをBuffer.from
に入れ,toString('base64')
で変換します.
このままだと,data-uriではないので,data:${response.headers['content-type'].toLowerCase()};base64,${image}
という形にして,data-uriとしてShareコンポーネントのurlプロパティに入れます.
これで元画像を白い帯などが付与されることなく共有できます.