こんばんわ。node.jsやってないとjavascriptのもぐりだと言われている昨今、いかがお過ごしでしょうか。
個人的には node.js は2回くらい飽きてしまって、3周目くらいです。
よく考えたらこのブログでは一度も node.js に触れてなかったなーと思ったのと、最近触ってないから忘れてそうだな...という思いからエントリにしてみました。
node.js が面白いと言われている理由の一つに非同期処理があります。そして非同期を面白くする題材として websocket があります。今日はその websocket を使って、サンプルを作ります。
物としては、twitter の filter stream から、instagr.am と picplz.com の画像URLを収集し、それをクライアントにブロードキャストします。クライアントはそれを受けて Growl 風にポップアップ表示するという物です。
まずサイトに仕上げるには、静的ファイルサーバが必要になります。
どのサイトでも紹介してそうですが、適当ながら動くレベルの物を作ります。
server.js と同じフォルダに config.json というファイルがあるので、そのファイル内を twitter アカウントで修正して頂ければ動きます。
mattn/node-image-stream - GitHub
display images from twitter stream
https://github.com/mattn/node-image-stream
みなさんもぜひ面白い物作ってみて下さい。
var sys = require('sys'),
fs = require('fs'),
url = require('url'),
http = require('http');
// static file server
var extmap = {'.htm': 'text/html
', '.css': 'text/css'};
var server = http.createServer(f
unction(req, res){
var uri = url.parse(req.url).p
athname;
if (uri.match('/$')) uri = '/in
dex.html'
try {
var filename = __dirname + '
/static' + uri;
var contenttype = extmap[fil
ename.substr(filename.lastIndexO
f("."), 4)] || 'application/octe
t-stream';
var rs = fs.createReadStream
(filename);
res.writeHead(200, {'Content
-Type': contenttype});
sys.pump(rs, res);
} catch(e) {
console.log(e);
res.sendHeader(404, {"Conten
t-Type": "text/plain"});
res.write("Not Found\n");
res.close();
}
});
server.listen(8080);
これを server.js というファイル名で保存して
# nod
e server.js
と実行すると、static フォルダ配下のファイルが http://localhost:8080/ でサーブされる仕組みです。
次に、websocket を扱える様にします。既存のサーバ上に websocket を乗せるには以下の様にサーバを指定すればokです。(おそらくこれは sugyan さんへのヒントになるのかもしれませんが)
var ws = websocket.createServer(
{server: server});
クライアント側の実装だとこんな感じですね。
var connection = new WebSocket('
ws://' + location.host);
connection.onopen = function(eve
nt) {
}
connection.onmessage = function(
event) {
}
さて、下準備は出来たので twitter stream を扱いましょう。twitter stream API はまだ basic 認証で扱えます。http-basic-auth モジュールを使いましょう。なお、Ubuntu 向けに launchpad から提供されている nodejs には node-waf が入っていませんので、依存で入る base64 モジュールのビルドに失敗します。どうしても試したい人は nodejs をソースからビルドしましょう。
twitter filter stream を扱うコードは以下の様になります。
var basicauth = require('http-ba
sic-auth');
var basicauthclient = basicauth.
createClient(80, 'stream.twitter.
com', false, false, account)
var req = basicauthclient.reques
t('GET', '/1/statuses/filter.jso
n?track=picplz,instagr', {'host'
: 'stream.twitter.com'})
req.end();
req.on('response', function (res)
{
res.on('data', function(chunk)
{
})
})
account は username と password をキーに持つオブジェクトです。ご自分のアカウントを設定しましょう。
twitter stream を受信したら、tweet の entities から URL 一覧を取得し、instagr.am と picplz.com の画像URLを取得しましょう。picplz.com はURLの後ろに /thumb/400 を足すだけですが、instagr.am は API にアクセスしなければなりません。
tweet 受信時の処理は以下の様になりました。
req.on('response', function (res)
{
res.on('data', function(chunk)
{
try {
var tweet = JSON.parse(chu
nk);
// parse entities urls
[].forEach.call(tweet.enti
ties.urls, function(item) {
// pick images of insta
gr.am or picplz.com
if (item.url.match('^htt
p://instagr.am/p/')) {
var client = http.cre
ateClient(80, 'instagr.am');
var req = client.requ
est('GET', '/api/v1/oembed/?form
at=json&maxheight=330&url=' + it
em.url, {'host': 'instagr.am'});
req.on('response', fu
nction(res){
res.on('data', func
tion(chunk){
var url = JSON.pa
rse(chunk).url
console.log(url)
ws.broadcast(url)
});
});
req.end();
}
if (item.url.match('^htt
p://picplz.com/')) {
var url = item.url +
'/thumb/400'
console.log(url)
ws.broadcast(url)
}
});
} catch(e) { console.log(e) }
});
});
サーバ部は出来上がりですね。最後にクライアント部を作りましょう。
$(function() {
var connection = new WebSocket
('ws://' + location.host);
connection.onopen = function(e
vent) {
$('#fotoflo').empty();
}
connection.onmessage = functio
n(event) {
var x = (Math.random() * ($(
document).width() - 330)).toFixe
d();
var y = (Math.random() * ($(
document).height() - 330)).toFix
ed();
var item = $('<div/>')
.addClass('popup-image')
.css({'left': x + 'px', 't
op': y + 'px', 'position': 'abso
lute', 'display': 'none'})
.appendTo('body')
$('<img/>')
.attr('src', event.data)
.bind('load', function() {
$(item).fadeIn(500).dela
y(10000).fadeOut(500, function()
{
$(this).remove()
})
}).appendTo(item)
}
})
こんな感じでしょうか。受信する度にランダムな位置へ画像をフェイドイン表示し、数秒経過したら消すというオーソドックスな物です。
以下、リポジトリの場所は示しますが、サーバの全体ソースを載せておきます。
var sys = require('sys'),
fs = require('fs'),
url = require('url'),
http = require('http'),
websocket = require('websock
et-server'),
basicauth = require('http-ba
sic-auth');
// load twitter account
var account = JSON.parse(fs.read
FileSync(__dirname + '/config.js
on', 'utf8'))
// static file server
var extmap = {'.htm': 'text/html
', '.css': 'text/css'};
var server = http.createServer(f
unction(req, res){
var uri = url.parse(req.url).p
athname;
if (uri.match('/$')) uri = '/in
dex.html'
try {
var filename = __dirname + '
/static' + uri;
var contenttype = extmap[fil
ename.substr(filename.lastIndexO
f("."), 4)] || 'application/octe
t-stream';
var rs = fs.createReadStream
(filename);
res.writeHead(200, {'Content
-Type': contenttype});
sys.pump(rs, res);
} catch(e) {
console.log(e);
res.sendHeader(404, {"Conten
t-Type": "text/plain"});
res.write("Not Found\n");
res.close();
}
});
server.listen(80);
// listen websocket server on th
e server
var ws = websocket.createServer(
{server: server});
// twitter filter stream
var basicauthclient = basicauth.
createClient(80, 'stream.twitter.
com', false, false, account)
var req = basicauthclient.reques
t('GET', '/1/statuses/filter.jso
n?track=picplz,instagr', {'host'
: 'stream.twitter.com'})
req.end();
req.on('response', function (res)
{
res.on('data', function(chunk)
{
try {
var tweet = JSON.parse(chu
nk);
// parse entities urls
[].forEach.call(tweet.enti
ties.urls, function(item) {
// pick images of insta
gr.am or picplz.com
if (item.url.match('^htt
p://instagr.am/p/')) {
var client = http.cre
ateClient(80, 'instagr.am');
var req = client.requ
est('GET', '/api/v1/oembed/?form
at=json&maxheight=330&url=' + it
em.url, {'host': 'instagr.am'});
req.on('response', fu
nction(res){
res.on('data', func
tion(chunk){
var url = JSON.pa
rse(chunk).url
console.log(url)
ws.broadcast(url)
});
});
req.end();
}
if (item.url.match('^htt
p://picplz.com/')) {
var url = item.url +
'/thumb/400'
console.log(url)
ws.broadcast(url)
}
});
} catch(e) { console.log(e) }
});
});
簡単ですね!
![imagestream](http://mattn.kaoriya.net/images/imagestream-thumb.png)