とあるデベロッパーが、JavaScriptだけでどのようにしてAirDropを再現したか
Post on:2020年12月24日
タップ・クリックするだけで他のデバイスと簡単にファイルを共有できるAirDropをJavaScriptだけで再現したその仕組みを紹介します。
同一WiFiで利用でき、iOSやmacOSだけでなく、AndroidやWindowsでもあらゆるファイルを簡単に共有できます。
How One Developer Recreated AirDrop Using Just JavaScript
by Abdisalan
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- はじめに
- 新しいテクノロジーの使用
- これがないとWebRTCは使用できない
- どのようにデータを保護しているか?
- 使い慣れたUIとUX
- デバイスがWebRTCをサポートしていない場合はどうなりますか?
- イベント駆動型のコードスタイル
- 自身でコードを調べる
- 終わりに
はじめに
iPhoneやMacで、AirDropを使用したことがありますか?
まだ使用したことがなければ、ボタンをタップするだけで、あらゆるファイルをスマホやデスクトップ間でシームレスに共有させることを想像してみてください。
Robin Linusによって作成されたSnapdropは、ブラウザを使用して任意のデバイス間でファイルを直接共有することができます。iPhoneとAndroidでも、タブレットとデスクトップでも簡単にファイルを共有できます。
クラウドへのアップロードやダウンロードは必要ありません😲
では、このSnapdropはどのように機能していると思いますか?
私はそれを一行一行調べた結果、その素晴らしいアーテクチャを理解しました。この記事では、Snapdropがどのように機能しているのか解説します。
新しいテクノロジーの使用
このテクノロジーを知っていると、それが提供するものをまだ知らないエンジニアと一線を画すことができるかもしれません。
その素晴らしいテクノロジーWebRTC(Web Real-Time Communication)で、数年前に登場したばかりです。このデータチャンネルにより、Snapdropはバイト(オーディオやビデオも!)をあるピアから別のピアに直接送信することができます。
※ピアとは、スマホやデスクトップなどのデバイスとして考えてください。
ただし、WebRTCは助けがなければ2つのデバイスを接続できません。つまり、他のピアを発見して接続方法を示すためのシグナリングサーバーが必要となります。
これがないとWebRTCは使用できない
WebRTCの概念図
ICE (Interactive Connectivity Establishment) とは、コンピューターがパブリックIPアドレスを持っていないときに、インターネットから自分自身にマップを描画する方法です。これは、ルーターとコンピューターの間で発生するNAT(ネットワークアドレス変換)によるものです。
マップが作成されると、2つのデバイスがお互いにマップを相互に共有する方法を見つける必要があります。Snapdropは、WebSocketを使用して各ピア間で通信するNodeJSサーバーを介してこれを行います。これはすばらしいプロトコルです。
あなたはおそらくこう考えるでしょう、これは安全ですか?
どのようにデータを保護しているか?
WebRTCはデータの転送中、デフォルトでデータを暗号します。それは素晴らしいことですが、ランダムな人とファイルを共有したくはないでしょう。
大丈夫です、Snapdropは同じIPアドレスを持つ2台のコンピュータ間でのみICEを共有します。つまり、同じネットワーク・Wi-Fi上でのみデータを共有します。
これは、IPアドレスごとにルームを作成し、ユニークなIDを生成することでデバイスを差別化しています。
1 2 3 4 5 6 7 8 9 10 |
/* サーバーからのピアへの参加を処理するコード */ _joinRoom(peer) { // ルームが存在しない場合は作成 if (!this._rooms[peer.ip]) { this._rooms[peer.ip] = {}; } // このピアをルームに追加 this._rooms[peer.ip][peer.id] = peer; } |
そのため、公共のWi-Fiを使用している場合はこのアプリを使用しない方がよいでしょう。でも、このパンデミックで誰が外出しますか?🤷
上記のコードは、サーバークラスのオブジェクトにピアを格納するという興味深い選択をしています。通常はデータベースが使用されることを期待しますが、これは物事を単純化するためかもしれません。アプリにはおそらく、多くのトラフィックがありません。
使い慣れたUIとUX
SnapdropのUIは、AirDropと同様にシンプルです。各デバイスには各ピアを区別するのに役立つ名前とアイコンがあります。それだけでなく、このアプリは下記のようないくつか優れた機能を提供するプログレッシブWebアプリです。
- ネイティブアプリのように動作
- 通知
- ライブアップデート
デバイスがWebRTCをサポートしていない場合はどうなりますか?
現在ではほとんどのデバイス・ブラウザがWebRTCをサポートしていますが、サポートしていない場合は、Snapdropにフォールバック機能があります。ファイルデータを送信するために、すでに確立されたWebSocket接続を使用します。
ただし、その場合はデータが最終目的地に到着する前に、最初にサーバーに送信される必要があるため、効率が悪く、安全性も低くなります。
1 2 3 4 5 |
if (window.isRtcSupported && peer.rtcSupported) { this.peers[peer.id] = new RTCPeer(this._server, peer.id); } else { this.peers[peer.id] = new WSPeer(this._server, peer.id); } |
イベント駆動型のコードスタイル
コードのベースは、完全にイベント駆動型です。このスタイルはサービスを相互に分離し、アクションの発生に応じた処理をできるようにする場合に使用します。
これは、WebRTCとWebSocketもイベント駆動型であるため、これらを補完します。メッセージが届いたり、新しいピアが参加したり、ファイルが送信されたりすると、それらがイベントになります。
直接的なプロセスではないため、最初は本当に大変です。イベントを登録して発生させるためのクラスは下記の通りです。
1 2 3 4 5 6 7 8 |
class Events { static fire(type, detail) { window.dispatchEvent(new CustomEvent(type, { detail: detail })); } static on(type, callback) { return window.addEventListener(type, callback, false); } } |
これにより、下記のようなイベント駆動型のコードを記述できます。
1 2 3 4 5 |
Events.on('signal', e => this._onMessage(e.detail)); Events.on('peers', e => this._onPeers(e.detail)); Events.on('files-selected', e => this._onFilesSelected(e.detail)); Events.on('send-text', e => this._onSendText(e.detail)); Events.on('peer-left', e => this._onPeerLeft(e.detail)); |
自身でコードを調べる
この記事で何かを学べることを願っています。
自分自身でコードを調べてみたい人は、GitHubリポジトリがあります。
作成者は親切にもDockerの構成ファイルを作成してくれたので、これを自分で実行してホストすることもできます。自分のSnapdropインスタンスを実行している人はどのくらいいるでしょうか?
終わりに
このプロジェクトは私にいくつかの貴重な教訓を教えてくれました。私はそれについて書かずにはいられませんでした。
またどこかで、お会いしましょう ✌
sponsors