徒然なるままに プログラミングメモや日々の生活などつれづれとつづっていくblog

2013年11月10日

Webサイトのキャプチャーを作る

Filed under: PHP,wkhtmltoimage,PC — ranpei @ 7:23 AM

現在私が管理しているWebサイトで、リンク先がサイト名だけではわかりにくいな~と思ったので、
マウスオーバーするとリンク先のイメージが表示されるような機能を付けることにしました。
その時のメモです。

今回の想定ではリアルタイムにスクリーンショットを作成する必要はないので(負荷が高くなるのでそもそもやりたくない)
wkhtmltoimageを利用して一定時間ごとにバッチでキャプチャーを撮る方式にしました。

wkhtmlimageのインストール

まず、wkhtmltoimageをインストールします。
インストールサーバーはUbuntu13.04(64bit)です。

1. ファイルをダウンロード

#wget http://wkhtmltopdf.googlecode.com/files/wkhtmltoimage-0.10.0_rc2-static-amd64.tar.bz2

最新のwkhtmltoimage-0.11.0_rc1-static-amd64.tar.bz2だとエラーが出てキャプチャーが取れないので
1つ前のバージョンをダウンロードします。

2. 解凍する

#tar jxf wkhtmltoimage-0.10.0_rc1-static-amd64.tar.bz2

解凍すると wkhtmltoimage-amd64 というファイルができます。
これがそのまま実行ファイルですので ./wkhtmltoimage-amd64 http://www.google.co.jp test.png とコマンドを実行すれば
キャプチャーが作成できるはずです。

もし、ライブラリが足りないなどのエラーが表示された場合は

#sudo apt-get install wkhtmltopdf

を実行して wkhtmltopdf をインストールしてみてください。
もともとwkhtmltopdfの機能だったものを分離しているのでwkhtmltopdfをインストールすれば
必要なライブラリ類も自動的にインストールされちゃうわけです。

3. /usr/bin/に配置

#mv wkhtmltoimage-0.10.0_rc1-static-amd64 /usr/bin/wkhtmltoimage

私はこれだけでパスの設定などは特に必要なかったです。

4. 日本語フォントのインストール
このままだと日本語が文字化けするので日本語フォントをインストールします。
今回はIPAフォントを利用しました。
ダウンロードしたものを解凍して .ttfファイルを/usr/share/fonts/配下に配置するだけです。

#sudo cp ipaexg00201/*.ttf /usr/share/fonts/.

以上が終わればインストール完了です。

#wkhtmltoimage http://www.google.co.jp test.png

とコマンドを実行すればキャプチャーが作成できているはずです。

ApacheOpenMeetings(TV会議システム)を試してみた

Filed under: ApacheOpenMeetings,OSS,TV会議,オープンソース — ranpei @ 2:32 AM

前回に引き続きTV会議システムの話題です。

今回ためしたのは「Apache OpenMeetings」です。
これもお手軽にからISOイメージを使ってインストールしました。
(もちろんESXi5.5にです・・)

ここからISOをダウンロードしてあとはインストールするだけです。
Ubuntuベースなのでインストールはすんなりいきました。

インストールしたらhttp://[インストールサーバーのIP]:5080/openmeetings/にアクセスすれば
ログインページが表示されます。
apacheOpenMeetings_Login

初期ログインIDである「toro/123456」を入力するとログインできます。
このユーザは言語設定が「英語」になっているので、管理 > ユーザ管理から言語設定を japanese に変更しておくと操作しやすいでしょう。
(新規ユーザを作ってもいいかも・・・)
あと、BBBと同じく共有ファイルの文字化けが起こったので
言語パッケージをインストールしておくとよいでしょう。

所感

さて、所感ですが。
このApache OpenMeetingsはBigBlueButtonと違い、
ユーザ管理や会議のスケジューリングといった機能がトータルにサポートされています。
機能面でいけばBigBlueButtonより充実しているといえますね。
ただし、共有ファイルをアップロードしてから表示するまでの時間がBigBlueButtonよりかなり遅いです。 

個人的にはBigBlueButtonのほうがUI的にもデザイン的にも好みですね。

2013年11月8日

BigBlueButton(TV会議システム)を試してみた

Filed under: BBB,BigBlueButton,OSS,TV会議,オープンソース — ranpei @ 12:16 AM

オープンソースのTV会議システムを見つけたので試してみた。

今回試してみたのはBigBlueButton(BBB)というツール
当初は試しにインストールして放置していたopenSUSE12.1にインストールしようとしたが
説明をよく読むとUbuntu10.04にしか対応していないっぽい。。。

Ubuntu10.04をインストールしてからセットアップするのも面倒なので
配布されているVMを利用することにしました。

インストール手順

BigBlueButtonのVm配布ページからZIPファイルをダウンロードしてきます。

解凍したものをEsxi5.5のデータストアにアップロードして新しいVMとしてインベントリに追加するのですが、
ZIPに入っているvmxファイルではうまく追加できなかった・・・
そのため新規にVMをHDDなしで作成して、そこにZIPのvmdkファイルを追加
そしたらうまく起動しました。

起動したらfirstuser/defaultでログイン
パスワードを再設定するように促されるので初期パスワードを入力して
新規パスワードを2回入力して再設定します。

このまま表示されるURLにブラウザでアクセスすればdemo画面が表示され、
TV会議を試すことができます。
bigbluebuttonDemo

ただ、このまま文書共有すると日本語が化けるので追加で言語パッケージをインストールしておいたほうがいいでしょう。

sudo apt-get install language-support-fonts-ja
sudo apt-get install language-support-ja
sudo apt-get install openoffice.org-l10n-ja
sudo apt-get install ttf-ipafont

所感

使ってみたところこのツールはTV会議の機能のみを提供して、
管理する機能はAPIを利用した外部ツールで行うようですね。
MoodleやwordpressなどのCMSツールにプラグインがあり、
redmineにも存在するようです。
redmineは仕事でも利用しているし、今度試してみようかな・・

2013年10月23日

VMDKファイルのコピーに失敗する

Filed under: ESXi,PC — ranpei @ 3:21 AM

ESXiアップデート時に起こった問題
「なぜかコピーできないVM(しかもこれがメインサーバーのVM)」
実はこれが一番原因を探るのに時間がかかりました。


・vSphere Client上でコピーしてもダメ、
・cpコマンドでコピーしてもダメ
・vmkfstoolsコマンドでクローンを移行先HDDに作ろうとしてもダメ


オプションをあれこれ変えながらいろいろ試行錯誤しましたが、どうにもならずギブアップ・・・
(コピーに時間がかかる上エラーとなるのが99%になったあたりだった)


結局、入れ替え先HDD上にVMを新規に作りそこに旧サーバーの設定やらDBデータやらを持っていく
いわゆる「サーバーの立て直し」することにしまいした。




立て直したサーバーは前のサーバーと同じvine linux 4.1 だったこともあり
以下の流れで結構すんなり移行することができました。
1. ユーザの移行
  ユーザ数が少なかったので新サーバーに手動でユーザを作り
  旧サーバーのユーザフォルダをガバーとコピーしてやりました。

2. mysqlのインストールと設定ファイルのコピー

3. 旧サーバーからmysqのデータをエクスポート→新サーバーにインポート
  mysqldumpコマンドで全データをエクスポート→インポート

4. apacheの設定ファイルと公開しているサイトのファイルをコピー

5. 新サーバーのIPアドレスを旧サーバーのものに変更する
  これが終われば速やかに旧サーバーは停止します


うちはWebサーバー以外立てていなかったためこれだけで済みましたが、
他にも色々なサーバーを立てている人は各サーバーの移行が必要でしょうね。


今回の「サーバーの立て直し」は苦肉の策、最終手段というところです。

ESXi 5.5 容量は問題ないのにコピーできない

Filed under: ESXi,PC — ranpei @ 3:21 AM

さて、前回のESXiアップグレード時に起こった問題の1つ
「容量は問題ないのに容量オーバーとなりコピーできない。」についてです。


この問題の原因はRawデバイスマッピング(RDM)したHDDでした。

/vmfs/volumes/524d5df7-3408bde8-5d18-d4ae52ca3c1d/windows7 # ls -l
total 43148312
-rw------- 1 root root 2000398934016 Oct 4 13:47 WDC_WD20EARX2D00PASB0-rdmp.vmdk
-rw------- 1 root root 539 Oct 5 18:22 WDC_WD20EARX2D00PASB0.vmdk
-rw------- 1 root root 132120576 Oct 5 16:39 vmx-windows7-1955177614-1.vswp
-rw------- 1 root root 1073741824 Oct 5 16:39 windows7-7489a48e.vswp
-rw------- 1 root root 27 Oct 3 15:30 windows7-f185cab4.hlog
-rw------- 1 root root 42949672960 Oct 22 17:20 windows7-flat.vmdk
-rw------- 1 root root 8684 Oct 20 15:51 windows7.nvram
-rw------- 1 root root 495 Oct 5 18:22 windows7.vmdk
-rw------- 1 root root 0 Oct 3 12:08 windows7.vmsd
-rw------- 1 root root 3658 Oct 22 09:46 windows7.vmx
-rw------- 1 root root 0 Oct 5 16:39 windows7.vmx.lck
-rw------- 1 root root 3937 Oct 5 18:23 windows7.vmxf
-rw------- 1 root root 3659 Oct 22 09:46 windows7.vmx~

上記の「WDC_WD20EARX2D00PASB0-rdmp.vmdk」がRDMしたvmdkです。
御覧の通りRDMしたHDDは見掛け上マウントしたHDDと同じ容量となっています。


どうやら、この見掛け上の容量が問題でコピー時に容量オーバーとなっていたようなのです。


vShere Client上でのデータコピー、SSH接続でcpコマンドを使用したコピーなどの方法を試しましたが
すべて容量エラーでコピーに失敗してしまいました。。。


結局入れ替え先のHDDにRDMのvmdkを新しく作ることにして移行しました。


備考ですが、ESXiでRMDを作成する方法はこちらをご覧ください。
vSphere(ESXi)でSATAディスクをRDMで使う

2013年10月8日

ESXi5.1 → 5.5 へのバージョンアップ

Filed under: ESXi,PC — ranpei @ 10:46 PM

3か月の出張を終えて帰ってまえりました。

戻ってきて即、出張している間に公開されたESXi5.5に自宅サーバーをアップグレードしてみましたw。

そしてみごとにはまり・・・・10/3~5の間サーバーにほとんど接続できなくなり、
訪れていただいた方々に非常にご迷惑をおかけしたことをこの場を借りて謝罪いたします。

さて、はまった内容ですが・・
実はESXiのアップグレードが原因ではなく、同時に行ったHDDの入れ替えがなかなかうまくいかなかったことが原因です。
1.容量は問題ないのに容量オーバーとなりコピーできない。
2.なぜかコピーできないVM(しかもこれがメインサーバーのVM)

この2つが同時に起こるものだから原因を特定するのにえらい時間がかかりました。
それぞれの詳細は別記事で書かせていただきます。

ではノシ

2013年7月7日

RSSリーダーを作ってみる その2

Filed under: PHP,アンテナサイト,ソース配布 — ranpei @ 1:38 AM

前回の公開したrssLoader
実はRSS2.0だとitemタグを解析できないというバグがある

どこが悪くて読み込めないかというと以下の部分

        // 各記事を取得する
        foreach($rssDom->item as $item) {
 
            $itemBean = new itemBean();

RSSのフォーマット・仕様・構造 – RSS1.0、RSS2.0、Content-Type」を見ていただくと分かるが
RSS1.0ではitemタグはchannelタグと同列のDom階層に存在するが
RSS2.0ではitemタグはchannelタグ配下のDom階層にある

RSS2.0を読み込むには「$rssDom->channel->item」としなければならないのだ

修正したソースは以下のようになる

<?php
require_once __DIR__."/channelBean.php";
require_once __DIR__."/itemBean.php";
require_once __DIR__."/rssException.php";
require_once __DIR__."/lib/httpConnecter.php";
require_once __DIR__."/lib/xmlLoader.php";

class rssLoader {

	const VERSION_1 = 1;
	const VERSION_2 = 2;

	const VERSION_ALL = 0;

	public function __construct() {
	}

	public function __destruct() {
	}

	/**
	 * RSSを読み込みchannelBeanで取得
	 *
	 * @param int $version 対象RSSバージョン指定
     *                    全バージョンのRSS  : rssLoader::VERSION_ALL
     *                    バージョン1.0のRSS : rssLoader::VERSION_1
     *                    バージョン2.0のRSS : rssLoader::VERSION_2
	 *
	 */
	public function loadRss($path, $version = self::VERSION_ALL) {

		/**
		 * RSSを取得
		 */
		$http = new httpConnecter();
		$http->connect($path);
		$rssStr = $http->loadText();
		if($rssStr === "") {

			// 取得できなければエラー
			throw rssException::notRss("RSSファイルの取得ができませんでした。");
		}


		/**
		 * RSSを解析
		 */
		$xml = new xmlLoader();
		$rssDom = $xml->parsor($rssStr);

		// すべてのバージョン
		if($version == self::VERSION_ALL) {

			try {

				return $this->loadRss2($rssDom);

			} catch (Exception $e) {

				return $this->loadRss1($rssDom);
			}
		}

		// rss1.0
		else if(isset($rssDom->attributes()->version) == false && $version == self::VERSION_1) {

			return $this->loadRss1($rssDom);
		}
		// rss2.0
		else if($rssDom->attributes()->version == 2 && $version == self::VERSION_2) {

			return $this->loadRss2($rssDom);
		}
		else {

			throw rssException::notMatchVersion("このRSSは正しい型式ではありません。");
		}

		return $channelBean;
	}

	/**
	 * RSS1.0を解析
	 * @param unknown $rssDom
	 */
	private function loadRss1($rssDom) {

		//if($rssDom->attributes()->version != self::VERSION_1) {

		//	throw rssException::notMatchVersion("このRSSはVertion1.0ではありません。");
		//}


		$channelBean = new channelBean();

		$channelBean->encoding = "UTF-8";
		$channelBean->rssVersion = self::VERSION_1;
		$channelBean->rssUrl = $rssDom->channel->link;


		// RSSの本体情報を取得
		if(isset($rssDom->channel->title)) {
			$channelBean->title = $rssDom->channel->title;
		}
		if(isset($rssDom->channel->link)) {
			$channelBean->link = $rssDom->channel->link;
		}
		if(isset($rssDom->channel->description)) {
			$channelBean->description = $rssDom->channel->description;
		}
		// dcの要素を取得
		$dc = $rssDom->channel->children('http://purl.org/dc/elements/1.1/');
		if(isset($dc->date)) {
			$channelBean->date = $dc->date;
		}
		if(isset($dc->language)) {
			$channelBean->language = $dc->language;
		}
		if(isset($dc->creator)) {
			$channelBean->creator = $dc->creator;
		}


		// ver1.0各記事を取得する
		foreach($rssDom->item as $item) {

			$itemBean = new itemBean();

			if(isset($item->title)) {
				$itemBean->title = $item->title;
			}
			if(isset($item->link)) {
				$itemBean->link = $item->link;
			}
			if(isset($item->description)) {
				$itemBean->description = $item->description;
			}
			if(isset($item->category)) {
				$itemBean->category = $item->category;
			}
			if(isset($item->pubDate)) {
				$itemBean->date = $item->date;
			}
			// dcの要素を取得
			$dc = $item->children('http://purl.org/dc/elements/1.1/');
			if(isset($dc->date)) {
				$itemBean->date = $dc->date;
			}
			if(isset($dc->subject)) {
				$itemBean->category = $dc->subject;
			}
			if(isset($dc->creator)) {
				$itemBean->creator = $dc->creator;
			}

			array_push($channelBean->itemList, $itemBean);
		}

		return $channelBean;
	}

	/**
	 * RSS2.0を解析
	 * @param unknown $rssDom
	 */
	private function loadRss2($rssDom) {

		if($rssDom->attributes()->version != self::VERSION_2) {

			throw rssException::notMatchVersion("このRSSはVertion2.0ではありません。");
		}

		$channelBean = new channelBean();

		$channelBean->encoding = "UTF-8";
		$channelBean->rssVersion = self::VERSION_2;
		$channelBean->rssUrl = $rssDom->channel->link;


		// RSSの本体情報を取得
		if(isset($rssDom->channel->title)) {
			$channelBean->title = $rssDom->channel->title;
		}
		if(isset($rssDom->channel->link)) {
			$channelBean->link = $rssDom->channel->link;
		}
		if(isset($rssDom->channel->description)) {
			$channelBean->description = $rssDom->channel->description;
		}
		// dcの要素を取得
		$dc = $rssDom->channel->children('http://purl.org/dc/elements/1.1/');
		if(isset($dc->date)) {
			$channelBean->date = $dc->date;
		}
		if(isset($dc->language)) {
			$channelBean->language = $dc->language;
		}
		if(isset($dc->creator)) {
			$channelBean->creator = $dc->creator;
		}


		// ver2.0各記事を取得する
		foreach($rssDom->channel->item as $item) {

			$itemBean = new itemBean();

			if(isset($item->title)) {
				$itemBean->title = $item->title;
			}
			if(isset($item->link)) {
				$itemBean->link = $item->link;
			}
			if(isset($item->description)) {
				$itemBean->description = $item->description;
			}
			if(isset($item->category)) {
				$itemBean->category = $item->category;
			}
			if(isset($item->pubDate)) {
				$itemBean->date = $item->date;
			}
			// dcの要素を取得
			$dc = $item->children('http://purl.org/dc/elements/1.1/');
			if(isset($dc->date)) {
				$itemBean->date = $dc->date;
			}
			if(isset($dc->subject)) {
				$itemBean->category = $dc->subject;
			}
			if(isset($dc->creator)) {
				$itemBean->creator = $dc->creator;
			}

			array_push($channelBean->itemList, $itemBean);
		}

		return $channelBean;
	}
}

上記ソースはほかにもネームスペース「dc」の要素が読み込めていない問題などを修正している

ダウンロードはこちらから→[download id=”1″]

2013年6月30日

アンテナサイト公開

Filed under: PHP,アンテナサイト — ranpei @ 7:19 AM

アンテナサイト作成の模様はまだ途中ですが
アンテナサイト自体は完成しているため公開します。

気ままアンテナ☆ミヾ(*´∇`)σ←今回作成したアンテナサイト
kimamaスクリーンショット

ファイルをローカルにダウンロードする

Filed under: PHP,アンテナサイト — ranpei @ 7:09 AM

前回の構成のためにRSSをダウンロードしてくる処理を作成してみます。

フォルダ構成は以下のようにしてみました。
antenaSiteDir

rssDownload.phpが本体でsite.csvから対象URLを取得してrssDataフォルダーにファイルを保存します。

以下がソースです。

<?php

define("RSS_DIR", "../rssData/");	// RSSデータのダウンロード先
define("CSV_FILE", "site.csv");		// RSS配信サイトのURLリスト
define("CRLF", "\r\n");

require_once __DIR__.'/rssLoader/rssLoader.php';

/**
 * CSVファイルから対象のRSSURL一覧を取得
 */
echo "load site csv start\n";

// CSVファイルをロード
$siteList = file(CSV_FILE, FILE_IGNORE_NEW_LINES);
if($siteList === false) {

	echo "not found ".CSV_FILE."\n";
	return;
}

echo "load site csv end\n";

/**
 * 各RSSダウンロード
 */
echo "download rss start\n";
$http = new httpConnecter();
try {

	$count = 0;
	foreach($siteList as $url) {

		echo "download url:".$url."\n";

		// 接続
		$http->connect($url);

		// 保存先ファイルオープン
		$filePath = sprintf("%s/%03d.rss", RSS_DIR, $count);
		$file = fopen($filePath, "wb");

		// ストリームでデータをロードし、ストリームで書き込む
		$buffer = null;
		do {

			// webからデータを取得する
			$buffer = $http->loadBinnaryStream(4086);

			// ファイルにデータを書き込む
			fwrite($file, $buffer);

			echo ".";

		} while(!is_null($buffer));
		echo "\n";

		// ファイルをクローズ
		fclose($file);

		$count++;
	}
} catch(Exception $e) {

	echo $e->getMessage()."\n";
}
echo "download rss end\n";

httpConnecterはrssLoaderが利用しているものを流用しています。
ぶっちゃけこれ使わなくてもいいんですけどね・・

これをcronで定期的回せば定期的にRSSを取得できます。

ソースのダウンロードはこちらから:[download id=”2″]

2013年6月28日

アンテナサイトの構成案

Filed under: PHP,アンテナサイト — ranpei @ 2:02 AM

前回、RSSをロードして情報を取得するクラスを作成した。
後はこのクラスを使って複数のRSS配信サイトからRSSを読み込んで表示するだけなのだが 、

ここで1つ注意すべき点がある。

それはアンテナサイトにアクセスがあるたび毎回RSS配信サイトにアクセスするような構成にしないことだ。

以下の図を見てほしい
アンテナサイトへのアクセスが少ない場合 アンテナサイトへのアクセスが多い場合
この用にアクセス数が増えればその分RSS配信サイトにも同じだけのトラフィック増加が起きることになる。
そしてサイトの表示時間も遅くなることが容易に想像だろう。

まあ、こんな注意いまさらって人が多いだろうが・・

だいたいの人は以下のように、RSSを取得するバッチと表示用の2つのプログラムを作成することになるだろう

antenaSite1

もしくはRSSを取得後静的なHTMLを吐き出すようにするって人もいるでしょう

antenaSite2

どちらも取得は一定時間ごとに行うのでRSS配信サイト側に係る負荷はアンテナサイトへのアクセス数が増えても増加しません。

また表示時間も 静的HTML > 表示用プログラム の順で早くなります。

今回は取得用と表示用に2つのプログラムを作成する方法を使って作成しようと思います。

では、次回に

« Newer PostsOlder Posts »

Powered by WordPress