Clound Foundry の Node で 動く Data URL Proxy Server を書いた
以前、GAE/J で同様のものを書いて利用していたのですが、大した処理じゃないのにスピンアップの時間が1〜2秒かかって初回レスポンスが遅いので、Cloud Foundry で動かせるように Node 版に書き換えてみました。Express 使うほどの処理ではないのですが、趣味と慣れの問題で使ってます。
こういう小物の Proxy や API サーバを置くには、Cloud Foundry (もしくは dotcloud) の Node がコードも短いし、インフラ整備もいらないので良いんじゃないかと思います。
ソース
base64 encoding images using express in NodeJS — Gist
/* * node-dataurl-proxy * * This code can run in Clound Foundry (http://cloudfoundry.com) * You can see example: http://dataurlproxy.cloudfoundry.com/?url=[url] */ require.paths.unshift('./node_modules'); var express = require('express'), URL = require('url'), http = require('http'); var app = module.exports = express.createServer(), port = process.env.VMC_APP_PORT || 3000; // Configuration app.configure(function(){ app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); // Routes app.get('/', function(req, res){ var url, client, proxyRequest; if (req.param('url')) { url = URL.parse(req.param('url')); client = http.createClient(80, url.hostname); client.on('error', function(err) { res.statusCode = 404; res.send(err.message); }); proxyRequest = client.request('GET', url.pathname, { "host": url.hostname }); proxyRequest.end(); proxyRequest.on('response', function(proxyResponse) { var type = proxyResponse.header('content-type'), prefix = 'data:' + type + ';base64,', body = '', success = true; proxyResponse.setEncoding('binary'); proxyResponse.on('data', function(chunk) { if (proxyResponse.statusCode === 200) { body += chunk; } else { res.statusCode = proxyResponse.statusCode; body += chunk; success = false; } }); proxyResponse.on('end', function() { if (success) { var base64 = new Buffer(body, 'binary').toString('base64'), data = prefix + base64, obj = { "src": url.href, "data": data }; res.contentType('application/json'); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header("Access-Control-Allow-Methods", "GET,POST"); res.send(JSON.stringify(obj)); } else { res.send(body); } }); }); } }); // Only listen on $ node app.js if (!module.parent) { app.listen(port); console.log("Express server listening on port %d", app.address().port); }
クライアント側のコード
jQuery を使うと以下のような感じです。
HTML
<canvas id="canvas"></canvas>
var proxy = 'http://yourappid.cloudfoundry.com/', imageUrl = "画像のURL", canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'); $.ajax({ url: proxy, type: 'GET', data: { url: imageUrl }, dataType: 'json', success: function(result) { var img = new Image(); img.onload = function() { ctx.drawImage(img, 0, 0, img.width, img.height); }; img.src = result.data; } });
Data URL Proxy ってどういう場面で必要なの?
HTML5 の Canvas は drawImage メソッドを利用することで、画像を表示することができます。 ただし、外部ドメインの画像を描画した場合、same-origin-policy 制約により、 描画コンテキストのピクセルデータにアクセスできません。(getImageData メソッドを呼び出すと、セキュリティエラー例外が発生します。)
DATA URL Proxy を利用することで、この制約を回避することができます。
ちなみにGAE/J 版はこれ
比較するとすごく簡単に書けているのがわかりますね。ファイル1つで済むし。