- PR -

複数ファイルのZip圧縮について

投稿者投稿内容
yopang
会議室デビュー日: 2005/05/11
投稿数: 3
投稿日時: 2005-05-11 12:05
複数のファイルを圧縮しています。
圧縮の際に1ファイルずつバイト数をバッファに読み込んでいきますが、
圧縮対象のファイルに0バイトファイルがあった場合にエラーは起きませんが、
Zipファイルを解凍すると、全てのファイルが0byteとなります。
普通の0byte以上のファイルのみだったら、正常に圧縮されます。

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
//ファイル名はarrayList[](配列)に格納しています。
//arrayList[0]="aaa.txt",arrayList[1]="bbb.txt"...

try {
//ZipOutputStreamを生成
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));

for (int i=0; i<arrayList.length; i++){
File file = new File(logDir+"/"+arrayList[i]);
//指定のファイルを読み込む
int len = (int)file.length();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), Math.max(1,len));

//ZipEntryを生成
ZipEntry ze = new ZipEntry(file.getName());
ze.setSize(len);

//書き込み
zos.putNextEntry(ze);

byte buf[] = new byte[(int) file.length()];

// 0KBのファイルを圧縮しようとした場合
if (len == 0) {
zos.write(buf, 0, buf.length);
}
// nKBのファイルを圧縮しようとした場合
else {
int count;
while ( (count = bis.read(buf, 0, len)) != -1) {
zos.write(buf, 0, buf.length);
}
}

bis.close();
zos.closeEntry();

}
zos.close();
} catch (Exception e){
e.printStackTrace();
}
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
上のソースで何か足りない部分がありましたら、補足のほうよろしくお願いします。

[ メッセージ編集済み 編集者: yopang 編集日時 2005-05-11 12:40 ]
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2005-05-11 13:19
使っていない変数や無駄な計算があちこちにありますが、本題ではないので放っておきます。

まず、0 バイトのファイルがなく圧縮/展開が正常に行われる場合と、0 バイトのファイルを含み展開結果が異常になる場合で、生成された圧縮ファイルのサイズに異常がないか確認してください。
後者の場合、前者の場合よりもファイルサイズが極端に小さくなっていませんか?
また、一つでも 0 バイトのファイルがあった場合、その前後の展開結果は全部 0 バイトになるのか、それとも前に限るとか後に限るとか、そのあたりの細かい再現分析も書かれた方がよろしいと思います。

こちらの手元では確認していませんが、
コード:

if (len == 0) {
zos.write(buf, 0, buf.length);
}


が怪しいように感じます。
この場合の buf.length は即ち file.length() であり len で、上のコードの場合zos.write(buf, 0, 0); という操作自体が無意味です。
(これが何もしないのであれば問題は起きないと思われますが、継承の大元であるjava.io.OutputStream#write(byte[], int, int) の API ドキュメントを見る限り、write(buf, 0, 0) の場合では最初のバイトが buf[0]、最後のバイトが buf[-1] となってしまい、好ましくないパラメータ指定だというのはわかると思います。)
あとはご自分で追跡調査をお願いします。

あと、これまた本題からずれますが、
コード:

else {
int count;
while ( (count = bis.read(buf, 0, len)) != -1) {
zos.write(buf, 0, buf.length);
}


バッファ経由コピーのイディオムに形は酷似していますが、意味を理解して使っているように見受けられません。
このコードだと圧縮するファイルの中に 1GB のものがあったら、メモリ上で 1GB の領域を確保しようとし、物理メモリがそれに満たない時に OutOfMemoryError で異常終了するかパフォーマンスが極端に落ちるか、何らかの障害が発生します。
(しかも count の値の使い途はファイル終端判別だけではありません。)

敢えて直接の回答は書きませんが、修正された方がよろしいでしょう。

[ メッセージ編集済み 編集者: Gio 編集日時 2005-05-11 13:21 ]
yopang
会議室デビュー日: 2005/05/11
投稿数: 3
投稿日時: 2005-05-11 15:12
ご返答ありがとうございます。

>まず、0 バイトのファイルがなく圧縮/展開が正常に行われる場合と、0 バイトのファイルを含み展開結果が異常になる場合で、生成された圧縮ファイルのサイズに異常がないか確認してください。
>後者の場合、前者の場合よりもファイルサイズが極端に小さくなっていませんか?
>また、一つでも 0 バイトのファイルがあった場合、その前後の展開結果は全部 0 バイトにな>るのか、それとも前に限るとか後に限るとか、そのあたりの細かい再現分析も書かれた方がよろしいと思います。

テストを行った結果、
データファイルが
aaa.txt -> 2byte
bbb.txt -> 1byte
の場合は、zipファイルサイズた1byteとなります。
展開すると結果は前者と一緒となります。

また、データファイルが
aaa.txt -> 2byte
bbb.txt -> 0byte
の場合は、
zipファイルサイズた1byteとなりますが、
展開するとファイルサイズはすべて0byteとなり、
aaa.txtの中身が消えてしまいます。

>こちらの手元では確認していませんが、
>コード:
>--------------------------------------------------------------------------------
>
>if (len == 0) {
>zos.write(buf, 0, buf.length);
>}
>
>
>--------------------------------------------------------------------------------
>
>が怪しいように感じます。
>この場合の buf.length は即ち file.length() であり len で、上のコードの場合>zos.write(buf, 0, 0); という操作自体が無意味です。
>(これが何もしないのであれば問題は起きないと思われますが、継承の大元である>java.io.OutputStream#write(byte[], int, int) の API ドキュメントを見る限り、>write(buf, 0, 0) の場合では最初のバイトが buf[0]、最後のバイトが buf[-1] となって>>しまい、好ましくないパラメータ指定だというのはわかると思います。)
>あとはご自分で追跡調査をお願いします。
ご指摘ありがとうございます。
もし、ファイルサイズが0の場合、
'zos.write(buf, 0, buf.length);'文を外して、そのまま次のファイル処理を行うことですね。(別に問題は起きないことを確認しました。)


そして、最後の問題は、確かに大容量のファイルの場合メモリエラー
が起きる可能性がありますね。
bufのサイズを多めにし、writeを実行する時、count分を書き込む形で宜しいでしょうか。


[ メッセージ編集済み 編集者: yopang 編集日時 2005-05-11 15:19 ]
びしばし
大ベテラン
会議室デビュー日: 2002/03/13
投稿数: 181
投稿日時: 2005-05-11 15:49
ファイルサイズ問題が解決したら次は文字化け問題で躓くかもしれませんので、あらかじめ貼っておきますね。

Java Solution 会議室 > Zipファイル作成時の文字化け
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=19724&forum=12&5

やまろうのJavaなわけ Vol.40 JavaでZIP圧縮。org.apache.tools.zipを使えば日本語ファイル名もOK
http://yamarou.at.infoseek.co.jp/javanawake/040.html
お犬様
ベテラン
会議室デビュー日: 2003/01/26
投稿数: 67
投稿日時: 2005-05-11 16:13
yopang さん wrote:
引用:
また、データファイルが
aaa.txt -> 2byte
bbb.txt -> 0byte
の場合

当方では圧縮できてます。ちなみに、
引用:
zipファイルサイズた1byteとなりますが、

と書いてありますが、当方では出来たzipファイルは全体で240バイトになってます。(ちなみに、圧縮後のサイズは aaa.txt が4バイト、bbb.txt が 2バイトです。圧縮メソッドを指定していない事、ZipOutputStream の圧縮方法から考えれば当然の結果ですが) 展開プログラムや検証プログラムに問題が無いか、unzip 等の第三者が作成したツールで圧縮直後のzipファイルに問題が無いかも確認してみてはいかがでしょうか?


ついでに。Gioさん wrote:
引用:
java.io.OutputStream#write(byte[], int, int) の API ドキュメントを見る限り、write(buf, 0, 0) の場合では最初のバイトが buf[0]、最後のバイトが buf[-1] となってしまい、好ましくないパラメータ指定だ

と書かれていますが、InputStream#read(byte[],int,int) では 第三引数が 0 の場合は読み込み処理を行わずに 0 を返せと書いてあります。InputStream と OutputStream の対称性から言って 0 が好ましくないパラメータだとは思えません。
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2005-05-11 16:29
引用する時、行頭に記号を加える形だと、ご覧になれる通りこの掲示板では位置がずれてしまうようです。
BB コードの QUOTE タグを使うのがよろしいかと思われます。

引用:

展開するとファイルサイズはすべて0byteとなり、
aaa.txtの中身が消えてしまいます。



最初 zos.write(buf, 0, 0); が悪さをして圧縮ストリームの状態をおかしくしているのではないかと考えましたが、それともやや違うようですね。
何はともあれ、
引用:

もし、ファイルサイズが0の場合、
'zos.write(buf, 0, buf.length);'文を外して、そのまま次のファイル処理を行うことですね。(別に問題は起きないことを確認しました。)


動作に要らないものは書かない、ということは重要です

最後にバッファサイズですが、
引用:

bufのサイズを多めにし、writeを実行する時、count分を書き込む形で宜しいでしょうか。


かなり突き放した書き方をしましたが、おわかりのようなので安心しました。
バッファサイズはファイルの長さによらず固定長、お書きの通り、read の返値をチェックした後、書き込む長さとして使うのがバッファ経由コピーのイディオムです。

どの程度の大きさのファイルを頻繁に扱うかですが、特に想定しない場合、私だと 8KB くらいにすることが多いです。
(後でパフォーマンスプロファイルを調べて、ここでボトルネックが発生しているようだと調整しますが)

Java ということで JDK バージョンや OS はお尋ねしませんでしたが、参考のため回答くだされば幸いです。
(同じ環境がこちらで再現できればですが、いろいろ追試するかもしれません。
し、しないかもしれません
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2005-05-11 16:40
引用:

InputStream と OutputStream の対称性から言って 0 が好ましくないパラメータだとは思えません。



失礼しました
OutputStream#write(byte[],int,int) の JavaDoc には第三引数が 0 の場合の動作について何も書かれていないため、byte[] の最初と最後のインデックスから不適切な指定と判断しました。
InputStream#read(byte[],int,int) と同様に第三引数が 0 の場合は何もしないと明示されていれば、無意味だが害はないコードとすぐにわかるのですが←言い訳(_ _)
yopang
会議室デビュー日: 2005/05/11
投稿数: 3
投稿日時: 2005-05-11 19:59
ファイルを修正したら、問題が直りました。
なお、解凍ツールはzh向けのものを使ったら展開に問題があったようで、
gzipのほうを試してみると、正常にファイルが展開されました。

Gio様へ、
開発環境は、OSはWindows2000(Service Pack4),jdkのバージョンは、1.4.2です。
bufのサイズは、1024くらいで大丈夫ですか。

色々ご指摘と、ご情報ありがとうございます。



[ メッセージ編集済み 編集者: yopang 編集日時 2005-05-11 20:00 ]

スキルアップ/キャリアアップ(JOB@IT)