29

XMLを含むzipファイルをダウンロードしていますが、待ち時間の要件があるため、zipファイルを操作する前にディスクに書き込むことは避けたいと思います。しかし、私にjava.util.zipは十分ではありません。ストリームに変換せずに「zipファイルのバイト配列を使用してください」と言う方法ZipInputStreamはなく、エントリヘッダーをスキャンするため、信頼性がありません(信頼性が低い理由については、以下の編集の説明を参照してください)。

処理するzipファイルにまだアクセスできないため、で処理できるかどうかわかりません。ZipInputStream有効なZIPファイルで機能するソリューションを見つける必要があります。生産に入った後の失敗に対するペナルティは高くなるからです。

ZipInputStreamが機能しないと仮定すると、エントリヘッダーがない場合に、この問題を解決するにはどうすればよいですか?私はウィキペディアの定義を使用しています。これには、標準としてzipファイル(以下に引用)を正しく解凍する方法に関するコメントが含まれています。

編集

Apache Commons Zipライブラリには、Stream(ソリューションとJavaの両方)を使用する際の問題のいくつかについての優れた記述があります。ウィキペディアと個人的な経験からさらに追加しますが、エントリヘッダーのサイズとcrcフィールドが入力されていない可能性があります(これらのフィールドに-1のファイルがあります)。このリンクを提供してくれたcenticに感謝します。

また、このテーマに関するウィキペディアを引用させてください。

zipアーカイブを正しく読み取るツールは、さまざまなフィールドの署名であるzip中央ディレクトリをスキャンする必要があります。ディレクトリだけがファイルチャンクの開始場所を指定するため、エントリをスキャンしてはなりません。このフォーマットでは、他のデータがチャンク間、またはそのようなシグニチャを含む非圧縮ストリームになることが禁止されていないため、スキャンによって誤検知が発生する可能性があります。

ZipInputStream中央ディレクトリではなく、エントリをスキャンすることに注意してください。これが問題です。

最終編集

興味のある方は、このスクリプトZipInputStreamを使用して、既存のZIPファイルから読み取ることができない有効なZIPファイルを作成できます。したがって、この閉じた質問の最終編集として、このスクリプトによって生成されたファイルなどのファイルを読み取ることができるライブラリが必要でした。

4

4 に答える 4

23

編集:別の提案...

Apache Commons の実装を見ると、プロジェクトでそれを効果的にフォークするのはそれほどZipFile難しくないように見えます。必要な APIのすべての部分を含むバイト配列の周りにラッパーを作成します(非常に多くはないと思います)。よりもインターフェースを好むことをすでに示しているので、それを使用しないのはなぜですか?RandomAccessFileZipFile

私たちはあなたのプロジェクトについて、これが法的な問題を引き起こすかどうかを判断するのに十分な情報を持っていません。あなたが詳細を提供したとしても、ここにいる誰かが適切な法的助言を与えることができるとは思えません.このソリューションを立ち上げて機能させるのに 1 時間か 2 時間かかりますが、あなたはそれにかなりの自信を持っていると思います。


編集:これはもう少し生産的な答えかもしれません...

エントリが連続していないことが心配であるが、すべての圧縮側を自分で処理したくない場合は、データを効果的に書き換えるオプションを検討できます。新しいByteArrayOutputStreamを作成し、最後に中央ディレクトリを読み取ります。中央ディレクトリのエントリごとに、エントリ (ヘッダー + データ) を出力ストリームに、適切と思われる形式で書き出しますZipInputStream。次に、新しい中央ディレクトリを作成します。置換を有効にする場合は、これを最初から行う必要がある場合がありますが、実際に中央ディレクトリを読み取らないことがわかっているコードを使用している場合は、元のディレクトリを提供するだけで済みます、有効でない可能性があるという事実を無視します。正しい署名で始まる限り、おそらくそれで十分です:)

それが終わったら、をnewByteArrayOutputStreamに変換し、それを a でラップしてからorに渡します。 byte[]ByteArrayInputStreamZipInputStreamZipArchiveInputStream

目的によっては、それほど多くのことを行う必要がない場合もあります。一度にディレクトリから読み取るエントリを 1 つだけ含む「ミニ」zip ファイルを作成することで、各ファイルを展開するだけでよい場合があります。 .

これにはzip ファイル形式を理解することが必要ですが、完全に理解する必要はありません。既存の API を完全に使用するような迅速かつ簡単な修正ではありませんが、それほど長くはかかりません。すべての無効なファイルを読み取れることを保証するものではありませんが (どうしてできるのでしょうか?)、特に懸念しているように思われる「エントリ間のデータ」の問題からは保護されます。それが少なくとも有用なアイデアであることを願っています...


「これが zip ファイルのバイト配列です。それを使用してください」と言う方法はありません。

はいあります:

byte[] data = ...;
ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
ZipInputStream zipStream = new ZipInputStream(byteStream);

それには、ZipInputStreamあなたが提供するすべてのzipファイルを処理できるかどうかの問題が残りますが、私はそれほどすぐにそれを書き留めません.

もちろん、他の API も利用できます。たとえば、Apache Commons Compressを見たいと思うかもしれません。ZipFileファイルが必要ですが、そうでZipArchiveInputStreamはありません。繰り返しますが、ByteArrayInputStream. 編集:中央ディレクトリからも読み取ってZipArchiveStream いないようです。事前に確認しておこうと思ったmarkSupportedのですが、そうでもないようです...

編集: 質問のコメントで、zip ファイルにエントリ データを含める必要がないことをどこで読んだかを尋ねました。あなたはウィキペディアを引用しました:

「zip アーカイブを正しく読み取るツールは、zip 中央ディレクトリのさまざまなフィールドの署名をスキャンする必要があります。ファイル チャンクの開始場所を指定するのはディレクトリのみであるため、エントリをスキャンしてはなりません。スキャンは誤検出につながる可能性があります。他のデータがチャンクの間にあること、またはそのような署名を含む圧縮されていないストリームを禁止しないでください。」

これは、入力データがオプションであることと同じではありません。エントリが完全に欠落している可能性があるわけではなく、扱いにくい場所に余分なデータがある可能性があると言っています。基本的に、エントリが連続していると見なされるべきではないと言っています。ファイルの末尾にある中央ディレクトリを読み取っていない可能性があることは喜んで認めることができZipInputStreamますが、それを行うコードを見つけることは、存在しないエントリ データに対処するコードを見つけることと同じではありません。

次に、次のように記述します。

さらに、zip が有効かどうかは気にしないことを付け加えておきます。それを扱うことです。

...これは、無効な zip ファイルを処理するコードが必要であることを示唆しています。これと組み合わせる:

これから扱うzipファイルにはまだアクセスできないので、ストリームで扱えるかどうかわかりません。

つまり、予測できない方法で無効な zip ファイルを処理するコードを求めているということです。あなたがそれを拒否することができるのは、どれほど無効である必要がありますか? ランダムな 1000 バイトを渡して、それらを zip ファイルにしようとはまったく考えていないとしたら、一体何をしますか?

基本的に、特定のライブラリが有効なソリューションであるかどうかを判断する前に、問題をより厳密に特定する必要があります。よく理解された方法で無効である可能性があるさまざまな場所から zip ファイルのセットを収集し、「これらすべてをサポートできなければならない」と言うのは理にかなっています。後で、それが十分でないことが判明した場合は、何らかの作業が必要になる場合があります。しかし、どんなに壊れていても、何でもサポートできるようにすることは、正当な要件ではありません。

于 2012-09-06T06:01:32.697 に答える
2

Apache ライブラリ commons-compress を使用します。http://commons.apache.org/compress/を参照してください。

ストリームを介した Zip ファイルの読み取りをサポートしています。詳細なドキュメントについては、 http://commons.apache.org/compress/zip.htmlに詳細なドキュメントがあります。また、Zip 形式に固有のいくつかの制限についても説明します。

サンプル コードは次のようになります。

ZipArchiveInputStream zip =
    new ZipArchiveInputStream(inputStream);
try {
    ZipArchiveEntry entry = zip.getNextZipEntry();
    while(entry != null) {
        assertEquals("README", entry.getName());
        ...
        entry = zip.getNextZipEntry();
    }
} finally {
    zip.close();
}
于 2012-09-11T10:11:49.653 に答える
2

TrueZIPライブラリは、代替の成熟した zip 実装を提供します。

また、HTTP のファイル システムの抽象化も備えています。

例えば:

Path path = new TPath(new URI("http://acme.com/download/everything.zip/entry.xml"));
try (InputStream in = Files.newInputStream(path)) {
    // Read archive entry contents here.
    ...
}

したがって、特定のエントリのみに関心がある場合は、それらのみをダウンロードして、帯域幅と時間を節約します. また、ダウンロード コードを記述する必要はありません。

http://truezip.java.net/faq.html#httpも参照してください。

于 2012-09-06T06:32:25.543 に答える
1

この質問は、メモリ内にディレクトリを作成する方法に似ていますか?疑似ファイルシステム/仮想ディレクトリ。基本的に、私の提案は、より一般的なソリューションであるインメモリ仮想ファイルシステムを使用することです(Linuxのramfs / tmpfsのようにOSレベルでは意味がありません)。

1つの例は、Java 7 NIO APIを使用することです。これにより、 FileSystemProviderを介してファイルシステムを実装するためのSPIが提供されます。ShrinkWrapファイルシステムがこのSPIを実装しているようです。

よりアクセスしやすいオプションは、Apache Commons VFSのRAMファイルシステムを使用することです。必要なのはJava5のみです。Java5および6との互換性が必要な場合は、これがおそらく最善の策です。

この記事からJavaのインメモリファイルシステムについて読んだことを最初に覚えています。これは、CommonsVFSやJBossMicrocontainerなどのソリューションを指摘する以外に、NetBeansIDEの優れたユースケースを示しています。

インメモリ仮想ファイルシステムは、OSレベルのファイルシステムを回避するための優れた一般的なソリューションですが(関連するパフォーマンス上の利点もあります)、より専門的なソリューションで対処できる他の欠点もある可能性があります。たとえば、このファイルシステムを使用すると、複数のスレッドから同時に使用した場合にどのように動作するかわかりません。同じファイルにアクセスしない限り、問題なく動作する可能性があります。または、個別のファイルシステムを作成する必要がある場合もあります(リソースの使用に関しては法外な場合があります)。

于 2012-09-13T07:50:23.160 に答える