簡體   English   中英

Java壓縮和解壓縮字節[]塊

[英]Java Compression & Decompression byte[] Chunks

像JDK Deflater / Inflater類允許傳遞byte []塊並將壓縮/未壓縮的值也作為byte []塊一樣(不需要輸入或輸出流),有人知道做這種事情的方法嗎, Zip文件? 這個想法是能夠按塊讀取輸入流並執行一種轉換管道:-入站:加密和壓縮-出站:解密和解壓縮

為了使用ZipInput / OutputStream類,我需要在加密/解密之前保存所有字節。

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));
    }
}

感謝@rob的建議,我終於找到了解決方案:

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));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM