- 締切済み
【ruby】zipファイルデータをSTDOUTに出力したい(file descriptorをSTDOUTに切り替えるには)
質問を見ていただいて有難うございます。 長文になりますが、宜しくお願いいたします。 最終的な質問をまとめると、 『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です。 ご指導のほど、宜しくお願いいたします。
- みんなの回答 (2)
- 専門家の回答
みんなの回答
- notnot
- ベストアンサー率47% (4900/10358)
>出来たところから、チャンクでソケットに書きたいと思いました。 出来たところからと言っても、圧縮ファイルの先頭に「圧縮後の長さ」を書かないと行けないので、ファイル単位でしか「出来」ませんよ。 つまり、細かいファイルたくさんを集めて圧縮するなら、ファイル単位で出力することで改善されますが、大きなファイル1つなら改善不可能。 いずれにせよ、やるとなると、大幅書き直しになるかと。
- notnot
- ベストアンサー率47% (4900/10358)
エラーメッセージからすると、ライブラリが内部でtellメソッドを使っているようですが、tellは、現在のファイル位置がファイル先頭から何バイト目かを返すメソッドで、ディスク上のファイルには使えますが、STDOUTには使えません。 いずれにせよ、処理に時間がかかってタイムアウトしているなら、出力方法をどう変えても同じだと思いますけど。圧縮に時間がかかっているのでは? 圧縮率を低く(無圧縮とか)すれば時間が短縮できると思います。
お礼
notnotさん ご回答有難うございます。 >いずれにせよ、処理に時間がかかってタイムアウトしているなら 今の方法だと、リクエストをもらってからレスポンスを開始するまでの 時間(無通信の時間)が長いので、クライアントがタイムアウトするものと思いまして。。。 出来たところから、チャンクでソケットに書きたいと思いました。
お礼
notnotさん、ご回答有難うございます。 細かいファイルをたくさん集めての出力なので。。。 >いずれにせよ、やるとなると、大幅書き直しになるかと。 そのようですね。 アドバイス有難うございました。