java.io.InputStream
圧縮されたデータが含まれていることを確認するための最良の方法は何ですか?
7 に答える
序章
すべての答えは5歳なので、私は書き留める義務を感じています。今日何が起こっているのか。私は、ストリームの魔法のバイトを読むべきだと真剣に疑っています!これは低レベルのコードであり、一般的には避ける必要があります。
簡単な答え
mikuは書いています:
ストリームをZipInputStream経由で読み取ることができる場合は、圧縮する必要があります。
はい。ただし、ZipInputStream
「読み取り可能」の場合は、最初の呼び出し.getNextEntry()
でnull以外の値が返されることを意味します。キャッチングなども例外ではありません。したがって、魔法のバイト解析の代わりに、次のことを実行できます。
boolean isZipped = new ZipInputStream(yourInputStream).getNextEntry() != null;
以上です!
一般的な解凍の考え
一般に、ストリームよりも[解凍]してファイルを操作する方がはるかに便利であるように見えました。いくつかの便利なライブラリがあり、さらにZipFileにはZipInputStreamよりも多くの機能があります。ここでは、zipファイルの処理について説明します。ファイルをzip/解凍するのに適したJavaライブラリは何ですか。したがって、ファイルを操作できる場合は、より適切に処理できます。
コードサンプル
アプリケーションでストリームのみを操作する必要がありました。これが私が解凍のために書いた方法です:
import org.apache.commons.io.IOUtils;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public boolean unzip(InputStream inputStream, File outputFolder) throws IOException {
ZipInputStream zis = new ZipInputStream(inputStream);
ZipEntry entry;
boolean isEmpty = true;
while ((entry = zis.getNextEntry()) != null) {
isEmpty = false;
File newFile = new File(outputFolder, entry.getName());
if (newFile.getParentFile().mkdirs() && !entry.isDirectory()) {
FileOutputStream fos = new FileOutputStream(newFile);
IOUtils.copy(zis, fos);
IOUtils.closeQuietly(fos);
}
}
IOUtils.closeQuietly(zis);
return !isEmpty;
}
ZIP形式のマジックバイト50 4B
はです。ストリームをテストすることはできますが(マークとリセットを使用して、バッファリングが必要になる場合があります)、これが100%信頼できるアプローチになるとは思いません。文字で始まるUS-ASCIIエンコードテキストファイルと区別する方法はありませんPK
。
最良の方法は、ストリームを開く前にコンテンツ形式でメタデータを提供し、それを適切に処理することです。
あまりエレガントではありませんが、信頼性があります。
ストリームを経由して読み取ることができる場合は、ZipInputStream
圧縮する必要があります。
ストリームの最初の4バイトが、ZIPファイル内のすべてのファイルを処理するローカルファイルヘッダーを開始するローカルファイルヘッダー署名であることを確認できます。これは、ここの仕様に示されています。50 4B 03 04
小さなテストコードは、これが機能することを示しています。
byte[] buffer = new byte[4];
try {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("so.zip"));
ZipEntry ze = new ZipEntry("HelloWorld.txt");
zos.putNextEntry(ze);
zos.write("Hello world".getBytes());
zos.close();
FileInputStream is = new FileInputStream("so.zip");
is.read(buffer);
is.close();
}
catch(IOException e) {
e.printStackTrace();
}
for (byte b : buffer) {
System.out.printf("%H ",b);
}
私にこの出力を与えました:
50 4B 3 4
魔法の数をチェックすることは正しいオプションではないかもしれません。
Docxファイルにも同様のマジックナンバー504B34があります
.zipと.xlsxの両方が同じマジックナンバーを持っているため、有効なzipファイルが見つかりませんでした(名前を変更した場合)。
そこで、ApacheTikaを使用して正確なドキュメントタイプを見つけました。
ファイルタイプの名前をzipに変更しても、正確なタイプが検出されます。
@McDowellと@Innokentyからの回答を、プロジェクトに貼り付けることができる小さなlib関数に結合しました。
public static boolean isZipStream(InputStream inputStream) {
if (inputStream == null || !inputStream.markSupported()) {
throw new IllegalArgumentException("InputStream must support mark-reset. Use BufferedInputstream()");
}
boolean isZipped = false;
try {
inputStream.mark(2048);
isZipped = new ZipInputStream(inputStream).getNextEntry() != null;
inputStream.reset();
} catch (IOException ex) {
// cannot be opend as zip.
}
return isZipped;
}
次のようにlibを使用できます。
public static void main(String[] args) {
InputStream inputStream = new BufferedInputStream(...);
if (isZipStream(inputStream)) {
// do zip processing using inputStream
} else {
// do non-zip processing using inputStream
}
}