Il modo migliore per rilevare se un flusso è compresso in Java
-
05-07-2019 - |
Domanda
Qual è il modo migliore per scoprire i java.io.InputStream
contiene dati compressi?
Soluzione
I byte magici per il formato ZIP sono 50 4B
. È possibile testare lo stream (utilizzando mark e reset - potrebbe essere necessario buffer ) ma Non mi aspetto che questo sia un approccio affidabile al 100%. Non ci sarebbe modo di distinguerlo da un file di testo con codifica US-ASCII che è iniziato con le lettere PK
.
Il migliore sarebbe fornire metadati sul formato del contenuto prima di aprire lo stream e quindi trattarlo in modo appropriato.
Altri suggerimenti
Introduzione
Dato che tutte le risposte hanno 5 anni, ho il dovere di scrivere, cosa sta succedendo oggi. Dubito seriamente che si dovrebbero leggere i byte magici del flusso! È un codice di basso livello, dovrebbe essere evitato in generale.
Risposta semplice
miku scrive:
Se lo Stream può essere letto tramite ZipInputStream, dovrebbe essere compresso.
Sì, ma in caso di ZipInputStream
" può essere letto " significa che la prima chiamata a .getNextEntry ()
restituisce un valore non nullo. Nessuna eccezione, ecc. Quindi, invece di analizzare i byte magici, puoi semplicemente fare:
boolean isZipped = new ZipInputStream(yourInputStream).getNextEntry() != null;
E questo è tutto!
Pensieri di decompressione generali
In generale, è sembrato che sia molto più comodo lavorare con i file mentre si [zippano], piuttosto che con i flussi. Esistono diverse utili librerie, inoltre ZipFile ha più funzionalità di ZipInputStream. La gestione dei file zip è discussa qui: Che cos'è un bene Libreria Java per comprimere / decomprimere i file? Quindi se riesci a lavorare con i file è meglio che tu faccia!
Esempio di codice
Nella mia applicazione avevo bisogno di lavorare solo con stream. Questo è il metodo che ho scritto per decomprimere:
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;
}
È possibile verificare che i primi quattro byte dello stream siano la firma dell'intestazione del file locale che avvia la intestazione del file locale che procede ogni file in un file ZIP, < a href = "http://www.pkware.com/documents/casestudies/APPNOTE.TXT" rel = "noreferrer"> come mostrato nelle specifiche qui essere 50 4B 03 04
.
Un piccolo codice di prova mostra che funziona:
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);
}
Mi ha dato questo output:
50 4B 3 4
Non molto elegante, ma affidabile:
Se lo Stream può essere letto tramite ZipInputStream
, dovrebbe essere compresso.
Controllare il numero magico potrebbe non essere l'opzione giusta.
Anche i file Docx hanno un simile numero magico 50 4B 3 4