Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
この記事に対してトラックバックを送信する(FC2ブログユーザー)
http://kovayashi.blog120.fc2.com/tb.php/71-e6700a45

-件のトラックバック

-件のコメント

コメントの投稿

投稿フォーム
投稿した内容は管理者にだけ閲覧出来ます

zipファイルデータをSTDOUTに出力

zipファイルを標準出力に書き出したい。

そこで、おしえて!goo質問

ズバリ回答は得られなかったが、その後なんとか自分で出来たので、整理する。

■経緯
 zipファイルを生成するライブラリは、探したところ
 ・rubyzip
 ・zipruby
 というのがある。

 共に標準出力への書き出し機能は備わっていないが、
 ziprubyには、Zip::Archive.open_bufferというメソッドがあるので、
 これを使って、
body=''
Zip::Archive.open_buffer(body, Zip::CREATE) do |ar|
 ar.add_buffer('first_entry.txt','Hello world!')
 ar.add_dir('adir')
 ar.add_buffer('adir/first_entry.txt','Hello again!')
end
print body

 などと書けば実現可能だが、zipファイルを全て生成し終えてから、
 ドカンと吐き出す仕様になる。

■出来たところから標準出力に吐き出すようにしたい。その1
 JAVAだと出来るようなので、JRubyに乗り換えて、JAVAのライブラリを
 呼ぶって線も検討したが、最終的にはcgiに組み込むつもりなので、
 その辺も考慮すると、
 JRubyでcgiを作るのは、そうとう面倒そうなうえ、
 借りてるレンタルサーバでは動かしようが無い
 ようなので、やめた。
 因みにJAVAだとこんな感じ。 こちらのページを参考にした。
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Zip {
public static void main(String[] args) throws IOException {
ZipOutputStream zos = new ZipOutputStream(System.out); //ここがミソ
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./Zip.java"));
ZipEntry entry = new ZipEntry("hoge/hoge.txt");
//entry.setSize(オリジナルのファイルサイズ);
//entry.setTime(オリジナルのタイムスタンプ);
zos.putNextEntry(entry);
byte buff[] = new byte[1024];
int readSize;
while ((readSize = bis.read (buff, 0, 1024)) != -1)
{
zos.write(buff, 0, readSize);
}
zos.closeEntry();
zos.close();
}
}

 ※エントリー生成毎に都度標準出力に吐き出されているかは未検証。

■【本題】出来たところから標準出力に吐き出すようにしたい。その2 
 rubyzipを利用して作る。

 ZipOutputStreamは、put_next_entryでエントリーのヘッダを作成。
 writeでエントリーのボディーを書き込み。
 全て書き終わってから、各エントリーのヘッダをファイルシークして
 更新する仕様になっている。(ソースを読んだ。)

 これを各エントリー毎に完結する様にし、その都度、標準出力に
 書き出しするようにする。

 スゲー大改造になるかと思ったが、オリジナルのソース(zip.rb)に
 手を入れることなく、ZipOutputStreamを継承した新しいクラスを作る事で実現できた。


require 'zip/zip'
module ZipKai
include Zip

class StdOut
def initialize()
@mem=''
@p=0
@out=0
end

def << (data)
return unless data
s=data.size
@mem[@p,s]=data
@p+=s
end

def tell
@out+@p
end

def pos=(x)
@p=x
end

def flush
STDOUT << @mem
@out+=@mem.size
@mem=''
@p=0
end

def close
STDOUT << @mem
end
end

class ZipOutputStreamKai < ZipOutputStream
include Zip

def initialize(fileName)
#super() ZipOutputStreamでsuperしてるが、その上位クラスに実はinitializeは無い
#@fileName = fileName
#@outputStream = File.new(@fileName, "wb")
@outputStream = StdOut.new
@entrySet = ZipEntrySet.new
@compressor = NullCompressor.instance
@closed = false
@currentEntry = nil
@comment = nil
end

def write(arg)
super(arg)
currentEntry=@currentEntry
finalize_current_entry
@outputStream.pos = 0
currentEntry.write_local_entry(@outputStream)
@outputStream.flush
end

def update_local_headers
end
end
end

STDOUT.binmode
ZipKai::ZipOutputStreamKai::open(nil) { |io|
io.put_next_entry("first_entry.txt")
io.write "Hello world!"
io.put_next_entry("adir/first_entry.txt")
io.write "Hello again!"
}


 二回include Zipしてるところが気に入ってないが、まあまあ自己満足。


※後日訂正
 @@memなどクラス変数になってたが、インスタンス変数の方がふさわしいので訂正した。
この記事に対してトラックバックを送信する(FC2ブログユーザー)
http://kovayashi.blog120.fc2.com/tb.php/71-e6700a45

0件のトラックバック

0件のコメント

コメントの投稿

投稿フォーム
投稿した内容は管理者にだけ閲覧出来ます

Appendix

プロフィール

kovayashi

Author:kovayashi
・嫌いなもの
 →好きなもの

・インストール
 →バンドル
・バックアップ
 →アップロード
・努力
 →チート

最近のトラックバック

ブログ内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。