Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

複数のテキストファイルのZIP化

初めて質問させていただきます。

JDK 1.5.0_15
Tomcat 5.5

現在、サーブレットで、以下のようなWEBアプリを作成中です。
? 画面で[データダウンロード]ボタンをクリック
? Oracle内の複数のテーブルを順に検索
? 取得したデータをテーブルごとにCSVファイルとして出力し、さらに、それらの複数のCSVファイルを1つのZIPファイルとしてダウンロードさせる

ただし、CSVもZIPファイルも一時ファイルなどを作成せず、サーバーのResponseとしてZIPファイルを生成し、そのZIPに各CSVを格納していく、というものです。

いろいろ試した結果、一時ファイルなどを使わずに、1つの
ZIPファイルに複数のCSVを含めることは出来たのですが、
ダウンロードされたZIPファイルを展開してCSVファイルの内容を
確認すると、レコード数が不足または全くない、という
状態になってしまいます。

CSV単体の生成→ダウンロードで検証したところ、その場合
にはCSVファイルの内容は正しくなっておりますので、クエリ文
などの問題ではありません。

以下にソース(抜粋)を掲載いたします。

(略)
// テーブル名リスト
ArrayList<String> tableList;
// 検索結果を格納
ArrayList<String> result;
 (略)
  ・
  ・
ZipOutputStream objZos
 = new ZipOutputStream(response.getOutputStream());

try {
 // ContentTypeを設定
 response.setContentType"application/zip; charset=Shift_JIS");response.setHeader( "Content-Disposition"," attachment;filename=\"data.zip\"");

 ByteArrayOutputStream objBos = null;
 OutputStreamWriter objOsw = null;

 for (Iterator<String> itr = tableList.iterator(); itr.hasNext();) {
   objBos = new ByteArrayOutputStream();
   objOsw = new OutputStreamWriter(objBos, "Shift_JIS");
     # (略)
     # テーブルごとに検索し、
     # resultに格納する処理
     # (略)
      for (int i = 0; i < result.size(); i++) {
                        lineData = result.get(i);
        objOsw.write(lineData.toString() + "\n");
      }
      ZipEntry objZe = new ZipEntry(fileName + ".csv");
      objZe.setMethod(ZipOutputStream.DEFLATED);
      objZos.putNextEntry(objZe);
      byte[] aryByt = objBos.toByteArray();
      objZos.write(aryByt, 0, aryByt.length);
    }
    objZos.closeEntry();

    objOsw.close();
    objBos.close();
    objZos.close();
  } catch (Exception e){
 ・
 ・
(略)


というようなソースですが、ZIP内のCSVには、本来の95%程度の行数しかないか、もしくは0行となってしまいます。

拙いプログラムしか書けない未熟者ですがよろしくお願いいたします。。

9

回答

16849

閲覧

9件の回答

評価

0

closeEntry()って、.putNextEntry()ごとに閉じるんじゃないの?

.close()はfinallyでやるべきだし。

評価

0

よく分からんけど、csvファイルの内容をwrite()しないで一体何をwrite()してるのか?

それに、closeEntry()の場所も違うみたい。

まず、objBosの内容がfileName.csvに書き出されるの? なにしろ、よーわからん。

評価

0

いや、DBから取ったcsvがobjBosのbyte[]に書き出されるから、それをresponseにかぶせたZipのOutputStreamへ流す。

よく見ればcloseEntry()だけじゃなくclose()もループの外だなあ。


>lineData.toString()
resultはList<String>なんだから、toString()するまでもなくStringだと思われ。

評価

0

あ、さらに。
Writerをclose()すれば、普通はその中のStreamもclose()される。

評価

0

掲示板に掲載用にソースの省略をした際に省略しすぎました。済みません。
実際には

ZipOutputStream objZos
 = new ZipOutputStream(response.getOutputStream());
 ByteArrayOutputStream objBos = null;
 OutputStreamWriter objOsw = null;

try {
 // ContentTypeを設定
 response.setContentType"application/zip; charset=Shift_JIS");response.setHeader( "Content-Disposition"," attachment;filename=\"data.zip\"");

 for (Iterator<String> itr = tableList.iterator(); itr.hasNext();) {
   objBos = new ByteArrayOutputStream();
   objOsw = new OutputStreamWriter(objBos, "Shift_JIS");
     # (略)
     # テーブルごとに検索し、
     # resultに格納する処理
     # (略)
       for (int i = 0; i < result.size(); i++) {
                        lineData = result.get(i);
          objOsw.write(lineData.toString() + "\n");
       }
       ZipEntry objZe = new ZipEntry(fileName + ".csv");
       objZe.setMethod(ZipOutputStream.DEFLATED);
       objZos.putNextEntry(objZe);
       byte[] aryByt = objBos.toByteArray();
       objZos.write(aryByt, 0, aryByt.length);
       //objZos.closeEntry(); //掲示板質問前には
       //     この位置でテスト済み→結果は同じでした
     }
     objZos.closeEntry();
  } catch (Exception e){
   ・
   ・
  } finally {
    if (objOsw != null) {
       objOsw.close();
    }
    if (objBos != null) {
       objBos.close();
    }
    if (objZos != null) {
       objZos.close();
    }
  }


という具合です。

objZos.closeEntry();の位置については、私も皆様と同様に考え、最初はループの中に入れていましたが、うまく行かなかったため、一度外に出し、結果が全く同じであったためそのままにしておりました。
ドキュメントには、
--------------------------------------
public void putNextEntry(ZipEntry e)
                  throws IOException
・・・新しい ZIP ファイルエントリの書き込みを開始し、エントリデータの開始位置にストリームを配置します。現在のエントリがアクティブである場合はそれを閉じます。
--------------------------------------
とありましたので、必ずしも毎回closeEntry()する必要はないのかな?と思っておりましたし。
やっぱりマズいでしょうか?

ちなみに、
? closeEntry()をループ内に配置 → 結果は変わらず
? objOsw.close()をループ内に配置 → 結果は変わらず
? objBos.close()をループ内に配置 → 結果は変わらず
という結果になりました。

>   byte[] aryByt = objBos.toByteArray();
>   objZos.write(aryByt, 0, aryByt.length);
このあたりが怪しいのでしょうか?

評価

0

あと一点、
>   lineData = result.get(i);
>   objOsw.write(lineData.toString() + "\n");
の部分につきましても、
実際には、lineData は StringBuffer で、
resultをあれこれ編集してCSV形式の1行にしたものと
なっております。
中途半端な省略(書き込み)を行ったために、余計な
疑問を抱かせてしまい申し訳ございません。

よろしくお願いいたします。

評価

0

今、うまく行きました!
  
----------------------
  //(ループ内)
  objOsw.close(); // ←【ここに持ってきた】
  ZipEntry objZe = new ZipEntry(fileName + ".csv");
  objZe.setMethod(ZipOutputStream.DEFLATED);
  objZos.putNextEntry(objZe);
  byte[] aryByt = objBos.toByteArray();
  objZos.write(aryByt, 0, aryByt.length);
} //ループ終了
objZos.closeEntry();
----------------------
objOsw.close(); の位置が悪かったようです。
皆様のアドバイスのおかげです。
本当にありがとうございました。
なお、うまく行ったとは言え、直すべきだという箇所に
ついてもさらにご教示いただければありがたく思います。

評価

0

要するに出力をflush(完全吐き出し)してなかったってことね。処理のストーリーがよく分かるコードを最初から投稿してくれたら、こっちも最初に気づいたでしょう。最初の時点では、なんだかよーわからんコードでした。

評価

0

>処理のストーリーがよく分かるコードを最初から投稿し>てくれたら
はい。
以後、気をつけます。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。