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つで済むし。