読者です 読者をやめる 読者になる 読者になる

by shigemk2

当面は技術的なことしか書かない

大阪Node学園一時限目 Learning a Node Stream

大阪Node学園一時限目(2012/1/28)

@kumatch
Feedtailor inc.
ObjectiveCを使って開発するのが中心だが、
サーバー側の担当
video game fun

通常のwebだと敢えてnodeを使う必要がないのが難点

Agenda

  • Summary
  • Usage
  • Case Study
  • Stream classes

Stream Summary
nodeの中のIOを抽象化したインターフェイスのこと
data streamを操作する操作のことをストリームという
docmentを見ると、すぐにstreamの記述場所が分かる
readable / writable stream
データの流れを表現するオブジェクトなので、
読み込み用と書き出し用のストリームが存在する。

How to copy files on Node
Node上でファイルをコピーするとき、どうしたらよいのか?
ファイルのモジュールにコピーのための機能があったりするが、
nodeではfileにアクセスするためのfsモジュールがある。
fsモジュールを利用してファイルを読み込み、自分でファイルをコピーする必
要がある。

var fs = require('fs);

var input = './original.jpg';
var output = './copy.jpg';

var data = fs.readFileSync(input);
fs.writeFileSync(output, data);
console.log('copied');

読み込みと書き出しが同期的に行われる。
処理中は他に何もできない。また、データの量も分からない。

nodeでは非同期で処理を行う必要になる。

var fs = require('fs);

var input = './original.jpg';
var output = './copy.jpg';

fs.readFile(input, function (err, data) {
    if(err) throw err;
    fs.writeFile(output, data, function(err) {
	if (err) throw err;

	console.log('copied');
    });
});


streamを使ってインターフェイスを利用する。

var fs = require('fs');
var readStream = fs.createReadStream('./original.jpg);
var writeStream = fs.createWriteStream('./copy.jpg');

readStream.pipe(writeStream);
writeStream.on('close', function() {
  console.log('copied');
});

Stream Usage
Stream is EventEmitter and has some methods.

ストリームオブジェクトはイベントエミッターである。
読み込みのためのメソッドと、書き込みのためのメソッドが存在する。

コピー元のファイルをストリームで読みこむ
readable stream

Methods

  • resume 読み込む処理を再開する
  • pause 読み込む処理を一旦停止する
  • destory 読み込む処理を停止する

Events

  • data データを読み込む
  • and 最後まで終わった場合発生する
  • close クローズ処理
  • error エラーが発生したとき

writable stream
Methods

  • write データを書き込む
  • end 書き込み処理を終了させる
  • destroy 書き込み処理を停止する

Events
drain writable streamの処理の準備が出来たときに発生する(IOが遅いときは、falseを返しdrainが失敗する)
close 書き込み処理を閉じるときに発生する
error エラーが発生したときに発生するイベント

var fs = require('fs');
var readStream = fs.createReadStream('./original.jpg');
var writeStream = fs.createWriteStream('./copy.jpg');
readStream.resume();
// 書き込み続ける
readStream.on('data',function (buffer) {
  writeStream.write(buffer);
});

// 読み込み終了
readStream.on('end',function(){
  writeStream.end();
});

// 書き込み終了(これが発生しないと、書き込みが終わらない)
writeStream.on('close', function() {
  console.log('copied');
});

Stream pipe()の機能について
Source stream(readable) pipe→ Destination stream(writable)

パイプを使って、読み込み側から書き込み側へデータを流す

pipeの処理内容は、以下3つを中心にして処理を続ける

書く
destination.write() if source on 'data'.

停止
source.pause() if destination buffer is full. 処理が間に合わないとかで
現在書き込み不能な場合

読み込み処理再開
source.resume() if destination on 'drain' 処理の準備が完了したとき

pipeのオプション
[optional]
Keeps the destination stream open.
書き込みが完了したら、closeにするが、
書き込み終了してもcloseしない。
(例: 複数のreadableストリームと一つの宛先が存在するとき、
一つの読み込み処理が終了してもcloseしない)

var fs = require('fs');
var readStream = fs.createReadStream('./original.jpg');
var writeStream = fs.createWriteStream('./copy.jpg');

readStream.pipe(writeStream);

// 書き込み終了(これが発生しないと、書き込みが終わらない)
writeStream.on('close', function() {
  console.log('copied');
});

読み込みが満杯ならストップし、準備が出来たら再開…を繰り返す。

Case Study 2

Digest SHA1 has of a file
これをnodeでやる場合は、やっぱりstreamを使う

var crypto = require('crypto');
var fs = require('fs');

var filename = process.argv[2];


var hash = crypto.createHash('sha1');
var stream = fs.ReadStream(filename);

// ハッシュをアップデート
stream.on('data', function(buffer) {
  hash.update(buffer);
});

// ハッシュからダイジェストへデータを渡しまくり、
// 結果(ハッシュ値計算)を出力する
stream.on('end', function() {
  var digest = hash.digest('hex');
  console.log(digest);
});

streamを使って、読み込んだデータをひたすら
hash計算し、コンソールに出力する

stream classes
処理の詳しい中身はそんなに知らなくてもいい
(ただエラーとかは把握すること)

  • Filesystem (fs)
    • readStream (読み込み用)
    • writeStream (書き込み用)
  • Net
    • net.Socket (読み込み用/書き込み用 どちらでも可)

与えられたデータが読み込み用でも書き込み用でもありうるときでも
対応できる

  • HTTP
    • http.ServerRequest (Readable)
    • http.ServerResponse (Writable)

サーバーが何かしら返す場合
リクエストはreadable
レスポンスはwritable

  • Zlib(圧縮・解凍系)
    • all classes (Readable/Writable)
      • Gzip/Gunzip
      • Deflate/Inflate
      • DeflateRaw/InflateRaw

ファイルを読み込み、gzipに圧縮して、圧縮したものを
レスポンスに書き出す、というストリームの実装も可能になる。

  1. fs.Readable
  2. pipe
  3. zlib.Gzip
  4. pipe
  5. http.response

streamを制する者はnodeを制す


References

メソッドチェインでstreamもどんどんできるよ。


マニュアルもあるから見てください。