プロが教えるわが家の防犯対策術!

質問を見ていただいて有難うございます。
長文になりますが、宜しくお願いいたします。

最終的な質問をまとめると、
『file descriptorをSTDOUTに切り替えるにはどうしたら良いですか?』
になるかと思いますが、目的から経緯についても以下に記します。

zipファイルを標準出力に書き出したいです。
プログラム上で作り出したzipファイル形式のデータを、
ディスクに書き出すのではなく、標準出力に書き出したいのです。

zipファイルをレスポンスするcgiを作りたいからです。

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

ziprubyには、Zip::Archive.open_bufferというメソッドがあったので、
これを利用して以下の様に書いてみました。(行頭スペースは全角です)

-------------------------------------------------------------
#!/usr/local/bin/ruby
require 'cgi'
require 'rubygems'
require 'zipruby'

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

CGI.new().out({
 'status'=>'OK',
 'type'=>'application/zip',
 'Content-Disposition'=>'attachment; filename="hoge.zip"'
}){body}
-------------------------------------------------------------
とりあえずは、これで目的を達成できたのですが、
巨大なzipファイル生成すると、クライアント(ブラウザ)がタイムアウトしてしまいます。
そこで、CGI.new().outを使うのをやめて、STDOUTに直接、”ストリーム”に書き出そうと思います。(Chunkで)

rubyzipには、Zip::ZipOutputStream::open(ファイル名)というのがあります。
これをファイルではなく、STDOUTへ出力するように改造を試みました。

zip.rbというソースファイルを見ると、
@outputStream = File.new(fileName, "wb")
という部分があるので、この@outputStreamをSTDOUTに変えてしまえばよいと考え、
以下の様に描いてみました。

(標準出力に書き出す部分のみです。)
-------------------------------------------------------------
require 'zip/zip'

module ZipKai
 include Zip

 class ZipOutputStreamKai < ZipOutputStream
  def initialize(fileName)
   super(fileName)
   @fileName = fileName
   #@outputStream = File.new(fileName, "wb")
   @outputStream = IO.new(1, "wb")
  end
 end
end

ZipKai::ZipOutputStreamKai::open("my.zip") { |io|
 io.put_next_entry("first_entry.txt")
 io.write "Hello world!"
 io.put_next_entry("adir/first_entry.txt")
 io.write "Hello again!"
}
-------------------------------------------------------------
実行してみると、以下エラーがでました。

C:/ruby/lib/ruby/gems/1.8/gems/rubyzip-0.9.1/lib/zip/zip.rb:1008:in `tell': Bad
file descriptor (Errno::EBADF)

ここで、質問です。
file descriptor(@outputStream)をSTDOUTに切り替えるにはどうしたら良いでしょうか?
※そもそも、他にもっと良い方法があるよといったご回答でもOKです。

ご指導のほど、宜しくお願いいたします。

A 回答 (2件)

>出来たところから、チャンクでソケットに書きたいと思いました。



出来たところからと言っても、圧縮ファイルの先頭に「圧縮後の長さ」を書かないと行けないので、ファイル単位でしか「出来」ませんよ。
つまり、細かいファイルたくさんを集めて圧縮するなら、ファイル単位で出力することで改善されますが、大きなファイル1つなら改善不可能。

いずれにせよ、やるとなると、大幅書き直しになるかと。
    • good
    • 0
この回答へのお礼

notnotさん、ご回答有難うございます。

細かいファイルをたくさん集めての出力なので。。。

>いずれにせよ、やるとなると、大幅書き直しになるかと。
そのようですね。

アドバイス有難うございました。

お礼日時:2009/09/23 20:42

エラーメッセージからすると、ライブラリが内部でtellメソッドを使っているようですが、tellは、現在のファイル位置がファイル先頭から何バイト目かを返すメソッドで、ディスク上のファイルには使えますが、STDOUTには使えません。



いずれにせよ、処理に時間がかかってタイムアウトしているなら、出力方法をどう変えても同じだと思いますけど。圧縮に時間がかかっているのでは?
圧縮率を低く(無圧縮とか)すれば時間が短縮できると思います。
    • good
    • 0
この回答へのお礼

notnotさん ご回答有難うございます。

>いずれにせよ、処理に時間がかかってタイムアウトしているなら
今の方法だと、リクエストをもらってからレスポンスを開始するまでの
時間(無通信の時間)が長いので、クライアントがタイムアウトするものと思いまして。。。

出来たところから、チャンクでソケットに書きたいと思いました。

お礼日時:2009/09/21 21:07

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!