• 締切済み

【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です。 ご指導のほど、宜しくお願いいたします。

みんなの回答

  • notnot
  • ベストアンサー率47% (4856/10272)
回答No.2

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

kingfruits
質問者

お礼

notnotさん、ご回答有難うございます。 細かいファイルをたくさん集めての出力なので。。。 >いずれにせよ、やるとなると、大幅書き直しになるかと。 そのようですね。 アドバイス有難うございました。

全文を見る
すると、全ての回答が全文表示されます。
  • notnot
  • ベストアンサー率47% (4856/10272)
回答No.1

エラーメッセージからすると、ライブラリが内部でtellメソッドを使っているようですが、tellは、現在のファイル位置がファイル先頭から何バイト目かを返すメソッドで、ディスク上のファイルには使えますが、STDOUTには使えません。 いずれにせよ、処理に時間がかかってタイムアウトしているなら、出力方法をどう変えても同じだと思いますけど。圧縮に時間がかかっているのでは? 圧縮率を低く(無圧縮とか)すれば時間が短縮できると思います。

kingfruits
質問者

お礼

notnotさん ご回答有難うございます。 >いずれにせよ、処理に時間がかかってタイムアウトしているなら 今の方法だと、リクエストをもらってからレスポンスを開始するまでの 時間(無通信の時間)が長いので、クライアントがタイムアウトするものと思いまして。。。 出来たところから、チャンクでソケットに書きたいと思いました。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • PythonでZIP中のZIPを操作する方法

    【PythonでZIPファイル中のZIPファイルを操作したい】 PythonでZIP内のZIPを再帰的に探して操作したいと考えています。 スクリプトを書いてみたのですが、どうもうまくいきません。 どなたかマズところをご教示いただけないでしょうか? 以下のような構造のデータファイルを用意しました。   SampleZip1.zip     Sample1.txt     Sample2.txt     SampleZip1-1.zip       Sample1-1-1.txt       Sample1-1-2.txt     SampleZip1-2.zip       Sample1-2-1.txt       Sample1-2-2.txt 以下がテストスクリプトです。   import zipfile      def listZipFile( fileName, indent ) :   if not zipfile.is_zipfile( fileName ) :     print( "not zip" + indent + fileName )   return      print( "zip" + indent + fileName )      zip = zipfile.ZipFile( fileName, 'r' )      for f in zip.namelist():     listZipFile( f, "¥t"+ indent )      zip.close()      zipFileName = 'SampleZip1.zip'   listZipFile( zipFileName, "¥t" ) が、結果は以下の通りで、ZIPの中のZIPをZIPファイルと判定してくれないみたいです。   >findZip.py   zip SampleZip1.zip   not zip SampleZip1-2.zip   not zip Sample1.txt   not zip Sample2.txt   not zip SampleZip1-1.zip ZIPファイル中のファイルに対してzipfile.ZipFile()を使うのは無理があるのかなぁ? 一時ファイルにでもいったん出さないとダメ? などと想像しているのですが・・・ どなたかよろしくお願いいたします。

  • Strutsによるバイナリファイル出力

    FileInputStream stream =new FileInputStream("C:/Test.exe"); String filename = "Test1.exe"; int bytesRead = 0; byte[] buffer = new byte[256]; HttpResponse.setContentType("application/octet-stream; charset=UTF-8"); HttpResponse.setHeader("Content-Disposition","attachment; filename=\""+filename+"\""); ServletOutputStream sos =HttpResponse.getOutputStream(); while ((bytesRead = stream.read(buffer,0,256) != -1) {  sos.write(buffer); } sos.flush(); sos.close(); stream.close(); 以上のコードでローカル内にあるファイルを名前を変え転送していますが クライアントにより正常なファイルだったり異常なファイルだったりします。 異常なファイルはクライアントによりばらばらですが、ファイルサイズが正常のものより少し大きかったりします。 バッファーではなくバイト単位での転送も試みたのですが 結果は同じでした。 何かおかしい点ありましたらご指摘いただけますでしょうか。

    • ベストアンサー
    • Java
  • zipフォルダの解凍

    いつもお世話になっています。 zipフォルダ実行時のことで質問します。 指定のファイルをzipフォルダに格納して、フォルダ名を指定して格納することはできました。 それで、逆に、zipフォルダにどんなファイルがあるかを確かめたいと思ったのですが、可能なのでしょうか? zipフォルダに格納できたので、それを逆に変えてしようとしたのですが、いまいちわかりません。 zipフォルダに格納したソースは以下です。 import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipOutStream_Test { public static void main(String[] args) throws Exception { File zipf = new File("D:/zip_Java.zip"); //zipファイルに埋め込むfile名 File[] files = { new File("directory.txt") }; ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipf)); try { encode(zos,files); } catch (IOException e) { }catch(Exception e) { }finally { zos.close(); } } static byte[] buf = new byte[1024]; public static void encode(ZipOutputStream zos, File[] files) throws Exception { for(File f: files) { if(f.isDirectory() ) { encode(zos, f.listFiles() ); }else { ZipEntry ze = new ZipEntry(f.getPath().replace('\\', '/')); zos.putNextEntry(ze); InputStream is = new BufferedInputStream(new FileInputStream(f)); for (;;) { int len = is.read(buf); if (len < 0) break; zos.write(buf, 0, len); } is.close(); } } } } 宜しくお願いします。

    • ベストアンサー
    • Java
  • 圧縮クライアントのプログラム

    java eclipeを使って通信のプログラムを勉強しています。 参考にしている本は、TCP/IPソケットプログラミングjava編です。 本に書かれてるソースコードのとおりに入力したら、 Exception in thread "main" java.io.FileNotFoundException: test.txt (No such file or directory) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(Unknown Source) at java.io.FileInputStream.<init>(Unknown Source) at socket_127.CompressClient.main(CompressClient.java:20) というエラーが出ました。 このエラーを調べると、 「指定されたパス名で示されるファイルが開けなかったことを通知します。」とかかれていました。このプログラムが入っているフォルダの中にtest.txtというテキストファイルを作成して同じフォルダに入れてあります。 このプログラムの引数は、 <Server> IPアドレス <Port>  110 <File> test.txt としました。 このエラーをとる方法を教えていただけないでしょうか? 下にソースコードを載せますので、解決方法をお願いします。 package socket_127; import java.net.*; // for Socket import java.io.*; // for IOException and [File]Input/OutputStream public class CompressClient { public static final int BUFSIZE = 256; // Size of read buffer public static void main(String[] args) throws IOException { if (args.length != 3) throw new IllegalArgumentException("Parameter(s): <Server> <Port> <File>"); String server = args[0]; int port = Integer.parseInt(args[1]); String filename = args[2]; FileInputStream fileIn = new FileInputStream(filename); FileOutputStream fileOut = new FileOutputStream(filename + ".gz"); Socket sock = new Socket(server, port); sendBytes(sock, fileIn); InputStream sockIn = sock.getInputStream(); int bytesRead; byte[] buffer = new byte[BUFSIZE]; while ((bytesRead = sockIn.read(buffer)) != -1) { fileOut.write(buffer, 0, bytesRead); System.out.print("R"); } System.out.println(); sock.close(); fileIn.close(); fileOut.close(); } private static void sendBytes(Socket sock, InputStream fileIn) throws IOException { OutputStream sockOut = sock.getOutputStream(); int bytesRead; byte[] buffer = new byte[BUFSIZE]; while ((bytesRead = fileIn.read(buffer)) != -1) { sockOut.write(buffer, 0, bytesRead); System.out.print("W"); } sock.shutdownOutput(); } }

  • PerlでZipファイルを作りたい

    PerlでZipファイルを作りたいのですが上手くいきません。 実行すると「Internal Server Error」になります。 Perlのバージョン:5.008009 文字コードは、Shift_JIS 改行コードは、Unix(LF)、です。 サーバーは「さくら」です。(ドリームホストも試しましたがダメでした) ファイル構成は zip.cgi data.txt 同じ階層に置いてます。 こちら https://www.h-fj.com/blog/archives/2010/07/19-203224.php のサイトを参考にさせてもらって下のように作ったのですが・・・ (print "Content-type: text/html¥n¥n";)は自分で勝手に 「いるんじゃない?」と思って付け加えました。 私、どこが間違っているのでしょうか? よろしくお願いします。 ———————————————————————————— zip.cgi ———————————————————————————— #!/usr/bin/perl use strict; use Archive::Zip qw( :ERROR_CODES :CONSTANTS ); my $zip = Archive::Zip->new(); # Zipのオブジェクト作成 $zip->addFile('data.txt'); # ファイルを圧縮 # Zipファイルに保存する print "Content-type: text/html¥n¥n"; if ($zip->writeToFileNamed('test.zip') == AZ_OK) { print "test.zip is successfully saved.¥n"; } else { print "test.zip save error.¥n"; } exit;

    • ベストアンサー
    • CGI
  • perl サブルーチンでのファイル出力結果おかしい

    以下のコードを実行するとカレントディレクトリの配下にある すべてのファイルのリストがコンソールとファイルに出力される はずですが、コンソールに表示されているファイルの一部しか ファイルに出力されていません。 どうも、最後に do_file()を呼び出したときのファイルしか リストされていないようなのですがなぜでしょうか。 どのようにすればよいのでしょうか。 よろしくお願いします。 (Windows7, ActivePerl(v5.16.3)) ----test.pl--------------------------------------------- &do_dir('.'); sub do_dir{  open(FILE2,'>list.txt') or die "$!";  my $dirname=shift;  my $delim='/';  opendir(DIR,$dirname) or die "$!";  foreach $entry (readdir(DIR)){   next if($entry eq '.');   next if($entry eq '..');   if ($dirname=~/[\\\/]$/) {    my $delim='';   }   my $filename="$dirname$delim$entry";   if(-d $filename){    &do_dir($filename);   } else {    &do_file($filename);   }  }  close(DIR);  close(FILE2); } sub do_file{  my $filename=shift;  return unless ($filename=~/\.*$/);  print "$filename\n";  print FILE2 "$filename\n"; }

    • ベストアンサー
    • Perl
  • PHP zipファイルのダウンロード

    下記のようなPHPスクリプトにおいて、zip フォルダに画像ファイルの圧縮ファイルを 保存し、ダウンロードしたいんです。ダウンロードの動作はChrome上で確認できてますが、圧縮した、ダウンロードファイルのサイズが0KB で、空なのです。 どこが間違っているか教えていただけますか? [file_zip.php] <?php mb_internal_encoding("UTF-8"); $ftp = ftp_connect("~"); ftp_login($ftp, "~", "~"); $dir = '/storage2/zip'; ftp_chdir($ftp, $dir); // ディレクトリ移動 // Zipクラスロード $zip = new ZipArchive(); // Zipファイル名 $zipFileName = $_POST['zip_filename']; var_dump($zipFileName); // Zipファイル一時保存ディレクトリ $zipTmpDir = '/storage2/zip'; // Zipファイルオープン $result = $zip->open($zipTmpDir.$zipFileName, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE); if ($result !== true) { // 失敗した時の処理 echo '圧縮ファイルをオープンできませんでした'; exit(); } // ここでDB等から画像イメージ配列を取ってくる $image_data_array = array(); array_unshift($image_data_array, $zipTmpDir.$zipFileName); // 処理制限時間を外す set_time_limit(0); foreach ($image_data_array as $filepath) { $filename = basename($filepath); // 取得ファイルをZipに追加していく $zip->addFromString($filename,file_get_contents($filepath)); } $zip->close(); // ストリームに出力 header('Content-Type: application/zip; name="' . $zipFileName . '"'); header('Content-Disposition: attachment; filename="' . $zipFileName . '"'); header('Content-Length: '.filesize($zipTmpDir.$zipFileName)); echo file_get_contents($zipTmpDir.$zipFileName); // 一時ファイルを削除しておく unlink($zipTmpDir.$zipFileName); // header("Location: storage.php"); ?>

    • 締切済み
    • PHP
  • ASP(VB)にて動的に作成したPDF(stream)をZIPへ格納後の展開ができない

    ASP(VB)にて動的に作成したPDF(stream)をZIPへ格納後、ダウンロードのプログラムを組んだのですが、ダウンロード後にZIPの中を展開するとPDFが 「ファイルの種類がサポートされていないか、または破損している可能性があります。例えば電子文書として送信され、正しくデコードされなかったことなどが考えられます。」 と表示されて開けません。 バイト数・圧縮率は正しく表示されているのですが何かソースとしてエンコード/デコード方法が足りてないのでしょうか? ご教授お願いします。 以下ソースです。( Ionic.Zip.dll使用) Dim teststream As Stream Dim streamRE As StreamReader = New StreamReader(\"C:\\test2.txt\") teststream = streamRE.BaseStream Response.Clear() Response.ContentType = \"application/zip\" Response.AddHeader(\"content-disposition\", \"filename=data.zip\") Dim zip As Ionic.Zip.ZipFile = New Ionic.Zip.ZipFile(Encoding.GetEncoding(\"shift_jis\")) Using (zip) zip.AddEntry(\"データ1.pdf\", \"\", teststream) \'ダウンロード出力 zip.Save(Response.OutputStream) End Using

  • Java・ファイルへの書き込み

    はじめまして。 下記のプログラムで、以下の3つがどのような関係なのかがわかりません。 ("test1.txt")とnew FileWriterの関係 (new FileWriter("test1.txt")とnew BufferedWriterの関係 (new BufferedWriter(new FileWriter("test1.txt")と new PrintWriterの関係 どうかご教示いただきたく、よろしくお願い致します。 import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class Sample7 { public static void main(String[] args){ PrintWriter pw = null; try{ pw = new PrintWriter (new BufferedWriter(new FileWriter("test1.txt"))); pw.println("Hello!"); pw.println("GoodBye"); System.out.println("ファイルに書き込みました。"); }catch(IOException e){ System.out.println("入出力エラーです。"); }finally{ if(pw != null){ pw.close(); } } } }

    • ベストアンサー
    • Java
  • Archive::Zipで指定ディレクトリに解凍したいのですが・・・

    お世話になっております。 Archive::Zipで以下のようなソースで解凍を行っています。 $file = "/data/31000/upload/MMEISYO.zip"; # オブジェクトの生成 my $zip = Archive::Zip->new(); #ファイルの読み込みに失敗したら強制終了 die 'Archive File Read Error' unless $zip->read($file) == AZ_OK; #ファイルの一覧を取得 @members = $zip->members(); foreach (@members) {   #ファイル名はfileNameにて取得できます。   my $name = $_->fileName();   #ファイルをアーカイブから取り出す。   $zip->extractMemberWithoutPaths($name); } しかしこれだと、CGIのあるディレクトリに解凍してしまいます。 違うディレクトリに解凍したい場合には、どのようにしたら よろしいのでしょうか? どなたかお助け願います。 お願いします。

    • ベストアンサー
    • Perl