2017-08-11 5 views
0

comme les classes JDK Deflater/Inflater qui permettent de passer des octets [] et d'obtenir la valeur compressée/non compressée en octets [] aussi (pas besoin de flux d'entrée ou de sortie), Est-ce que quelqu'un sait d'une façon de faire la même chose, mais pour les fichiers Zip? L'idée est de pouvoir lire un flux d'entrée par morceaux et faire une sorte de pipeline de transformation: - Inbound: Crypter et compresser - Outbound: Décrypter et décomprimerJava Compression & Decompression byte [] Morceaux

Avec les classes ZipInput/OutputStream afin de faire que j'ai besoin de sauvegarder tous les octets avant de crypter/décrypter.

import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.util.zip.DataFormatException; 
import java.util.zip.Deflater; 
import java.util.zip.Inflater; 

public class Compression { 

    public static void main(String[] args) throws IOException, DataFormatException { 
     final int bufferSize = 1024; 
     byte[] uncompressedChunkBuffer = new byte[bufferSize]; 
     int uncompressedChunkLength = 0; 
     byte[] compressedChunkBuffer = new byte[bufferSize]; 
     int compressedChunkLength = 0; 
     //Compression 
     Deflater deflater = new Deflater(); 
     String uncompressedText = randomText(); 
     byte[] expectedUncompressedBytes = uncompressedText.getBytes(); 
     System.out.println("Bytes Length: " + expectedUncompressedBytes.length); 
     ByteArrayInputStream uncompressedBytesInStream = new ByteArrayInputStream(expectedUncompressedBytes); 
     ByteArrayOutputStream compressedBytesOutStream = new ByteArrayOutputStream(); 
     while ((uncompressedChunkLength = uncompressedBytesInStream.read(uncompressedChunkBuffer)) != -1) { 
      //This part allows to set and get byte[] chunks 
      deflater.setInput(uncompressedChunkBuffer, 0, uncompressedChunkLength); 
      while (!deflater.needsInput()) { 
       compressedChunkLength = deflater.deflate(compressedChunkBuffer); 
       if (compressedChunkLength > 0) { 
        compressedBytesOutStream.write(compressedChunkBuffer, 0, compressedChunkLength); 
       } 
      } 
     } 
     deflater.finish(); 
     while (!deflater.finished()) { 
      compressedChunkLength = deflater.deflate(compressedChunkBuffer); 
      if (compressedChunkLength > 0) { 
       compressedBytesOutStream.write(compressedChunkBuffer, 0, compressedChunkLength); 
      } 
     } 
     deflater.end(); 
     uncompressedBytesInStream.close(); 
     compressedBytesOutStream.flush(); 
     compressedBytesOutStream.close(); 
     byte[] compressedBytes = compressedBytesOutStream.toByteArray(); 
     System.out.println("Compressed Bytes Length: " + compressedBytes.length); 
     //Decompression 
     Inflater inflater = new Inflater(); 
     ByteArrayInputStream compressedBytesInStream = new ByteArrayInputStream(compressedBytes); 
     ByteArrayOutputStream uncompressedBytesOutStream = new ByteArrayOutputStream(); 
     while ((compressedChunkLength = compressedBytesInStream.read(compressedChunkBuffer)) != -1) { 
      //This part allows to set and get byte[] chunks 
      inflater.setInput(compressedChunkBuffer, 0, compressedChunkLength); 
      while ((uncompressedChunkLength = inflater.inflate(uncompressedChunkBuffer)) > 0) { 
       uncompressedBytesOutStream.write(uncompressedChunkBuffer, 0, uncompressedChunkLength); 
      } 
     } 
     while ((uncompressedChunkLength = inflater.inflate(uncompressedChunkBuffer)) > 0) { 
      uncompressedBytesOutStream.write(uncompressedChunkBuffer, 0, uncompressedChunkLength); 
     } 
     inflater.end(); 
     compressedBytesInStream.close(); 
     uncompressedBytesOutStream.flush(); 
     uncompressedBytesOutStream.close(); 
     byte[] actualUncompressedBytes = uncompressedBytesOutStream.toByteArray(); 
     System.out.println("Uncompressed Bytes Length: Expected[" + expectedUncompressedBytes.length + "], Actual [" + actualUncompressedBytes.length + "]"); 
    } 

    public static String randomText() { 
     StringBuilder sb = new StringBuilder(); 
     int textLength = rnd(100, 999); 
     for (int i = 0; i < textLength; i++) { 
      if (rnd(0, 1) == 0) { 
       sb.append((char) rnd(65, 90)); 
      } else { 
       sb.append((char) rnd(49, 57)); 
      } 
     } 
     return sb.toString(); 
    } 

    public static int rnd(int min, int max) { 
     return min + (int) (Math.random() * ((max - min) + 1)); 
    } 
} 
+1

L'idée de InputStream et OutputStream sont que vous pouvez créer vos propres sous-classes qui enveloppent d'autres cours d'eau. Cela permet à votre sous-classe d'accéder aux données lors de leur diffusion (jusqu'à un octet à la fois). – Rob

Répondre

0

Merci à @rob suggestion que je finalement parvenues à une solution:

private static final String SECRET_KEY_ALGO = "AES"; 
private static final int SECRET_KEY_SIZE_IN_BITS = 256; 
private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding"; 
private static final int DEFAULT_BUFFERSIZE = 8 * 1024; 

public static void main(String[] args) throws IOException { 
    String expected = randomText(); 
    byte[] textBytes = expected.getBytes(); 
    EncryptedOutputStreamWrapper enc = new EncryptedOutputStreamWrapper(); 
    { 
     InputStream in = new ByteArrayInputStream(textBytes); 
     ZipOutputStream out = new ZipOutputStream(enc.wrap(new FileOutputStream("f.zip"))); 
     out.putNextEntry(new ZipEntry("_")); 
     IOUtils.copy(in, out); 
     in.close(); 
     out.closeEntry(); 
     out.close(); 
    } 
    // 
    DecryptedInputStreamWrapper dec = new DecryptedInputStreamWrapper(enc.getSKey(), enc.getIv()); 
    { 
     ZipInputStream in = new ZipInputStream(dec.wrap(new FileInputStream("f.zip"))); 
     OutputStream out = new FileOutputStream("f.txt"); 
     in.getNextEntry(); 
     IOUtils.copy(in, out); 
     in.closeEntry(); 
     in.close(); 
     out.close(); 
    } 
    // 
    String actual = new String(IOUtils.toByteArray(new FileInputStream("f.txt"))); 
    if (!expected.equals(actual)) { 
     System.out.println("Fail!"); 
     System.out.println("Expected '" + expected + "'"); 
     System.out.println(); 
     System.out.println("Actual: '" + actual + "'"); 
    } else { 
     System.out.println("Success!"); 
    } 
} 

public static class EncryptedOutputStreamWrapper { 
    private Cipher cipher; 
    private SecretKey sKey; 
    private byte[] iv; 

    public EncryptedOutputStreamWrapper() { 
     try { 
      KeyGenerator generator = KeyGenerator.getInstance(SECRET_KEY_ALGO); 
      generator.init(SECRET_KEY_SIZE_IN_BITS); 
      this.sKey = generator.generateKey(); 
      this.cipher = Cipher.getInstance(AES_TRANSFORMATION); 
      this.cipher.init(Cipher.ENCRYPT_MODE, sKey); 
      this.iv = cipher.getIV(); 
     } catch (Exception e) { 
      throw new CipherException("Error encrypting", e); 
     } 
    } 

    public OutputStream wrap(final OutputStream out) { 
     return new BufferedOutputStream(new OutputStream() { 
      @Override 
      public void write(int b) throws IOException { 
      } 

      @Override 
      public void write(byte[] plainBytes, int off, int len) throws IOException { 
       byte[] encryptedBytes = cipher.update(plainBytes, off, len); 
       if (encryptedBytes != null) { 
        out.write(encryptedBytes, 0, encryptedBytes.length); 
       } 
      } 

      @Override 
      public void flush() throws IOException { 
       out.flush(); 
      } 

      @Override 
      public void close() throws IOException { 
       try { 
        byte[] encryptedBytes = cipher.doFinal(); 
        if (encryptedBytes != null) { 
         out.write(encryptedBytes, 0, encryptedBytes.length); 
        } 
       } catch (Exception e) { 
        throw new IOException("Error encrypting", e); 
       } 
       out.close(); 
      } 
     }); 
    } 

    public SecretKey getSKey() { 
     return sKey; 
    } 

    public byte[] getIv() { 
     return iv; 
    } 

} 

public static class DecryptedInputStreamWrapper { 
    private Cipher cipher; 

    public DecryptedInputStreamWrapper(SecretKey sKey, byte[] iv) { 
     try { 
      this.cipher = Cipher.getInstance(AES_TRANSFORMATION); 
      this.cipher.init(Cipher.DECRYPT_MODE, sKey, new IvParameterSpec(iv)); 
     } catch (Exception e) { 
      throw new CipherException("Error decrypting", e); 
     } 
    } 

    public InputStream wrap(final InputStream in) { 
     return new BufferedInputStream(new InputStream() { 
      private byte[] buffer = new byte[DEFAULT_BUFFERSIZE]; 
      private boolean done; 

      @Override 
      public int read() throws IOException { 
       return 0; 
      } 

      @Override 
      public int read(byte[] bytes, int off, int len) throws IOException { 
       if (done) { 
        return -1; 
       } 
       int encryptedLen = in.read(buffer); 
       try { 
        byte[] plainBytes = null; 
        if (encryptedLen == -1) { 
         done = true; 
         plainBytes = cipher.doFinal(); 
        } else { 
         plainBytes = cipher.update(buffer, 0, encryptedLen); 
        } 
        if (plainBytes != null) { 
         System.arraycopy(plainBytes, 0, bytes, off, plainBytes.length); 
         return plainBytes.length; 
        } 
       } catch (Exception e) { 
        throw new IOException("Error decrypting", e); 
       } 
       return 0; 
      } 

      @Override 
      public void close() throws IOException { 
       in.close(); 
      } 

     }); 
    } 
} 

public static class CipherException extends RuntimeException { 
    private static final long serialVersionUID = 1L; 

    public CipherException() { 
     super(); 
    } 

    public CipherException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 
     super(message, cause, enableSuppression, writableStackTrace); 
    } 

    public CipherException(String message, Throwable cause) { 
     super(message, cause); 
    } 

    public CipherException(String message) { 
     super(message); 
    } 

    public CipherException(Throwable cause) { 
     super(cause); 
    } 

} 

public static String randomText() { 
    StringBuilder sb = new StringBuilder(); 
    int textLength = rnd(100000, 999999); 
    for (int i = 0; i < textLength; i++) { 
     if (rnd(0, 1) == 0) { 
      sb.append((char) rnd(65, 90)); 
     } else { 
      sb.append((char) rnd(49, 57)); 
     } 
    } 
    return sb.toString(); 
} 

public static int rnd(int min, int max) { 
    return min + (int) (Math.random() * ((max - min) + 1)); 
} 
+0

L'utilisation de ** javax.crypto.CipherInputStream ** et ** javax.crypto.CipherOutputStream ** est plus logique – bajistaman