java gzip 內存壓縮

java 網絡通訊中,爲了節省帶寬,可能需要將數據壓縮之後再傳輸,正常過程如下

1. 讀取原始文件 IO流

2. 用壓縮工具壓縮寫入文件

3. 讀取壓縮文件IO 流

4. 傳輸壓縮 IO 流

5. 刪除壓縮文件

這樣一來就涉及到臨時壓縮文件的保存,保存完還需要刪除壓縮文件, 爲了方便,我們直接將2、5步去除,直接將原始文件IO流壓縮成壓縮流,然後傳輸


代碼如下(代碼依賴於 Apache Commons Compress )

下載地址:http://commons.apache.org/proper/commons-compress/download_compress.cgi


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
import org.apache.commons.compress.compressors.CompressorOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipParameters;
import org.apache.commons.compress.utils.CharsetNames;

/**
 * gzip 壓縮二進制,不寫文件並返回二進制
 * 修改自 org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
 * @author ysq
 *
 */
public class GzipByteUtil extends CompressorOutputStream {

    /** Header flag indicating a file name follows the header */
    private static final int FNAME = 1 << 3;

    /** Header flag indicating a comment follows the header */
    private static final int FCOMMENT = 1 << 4;

    /** The underlying stream 因爲該方法不需要寫文件,所以不需要輸出 */
    //private OutputStream out;

    /** Deflater used to compress the data */
    private final Deflater deflater;

    /** The buffer receiving the compressed data from the deflater */
    private final byte[] deflateBuffer = new byte[512];

    /** Indicates if the stream has been closed */
    private boolean closed;

    /** The checksum of the uncompressed data */
    private final CRC32 crc = new CRC32();
    
    private byte[] compressedByte;


	/**
     * Creates a gzip compressed output stream with the default parameters.
     */
    public GzipByteUtil() throws IOException {
        this(new GzipParameters());
    }

    /**
     * 不需要寫文件所以不需要 out 對象
     * 
     * @since 1.7
     */
    public GzipByteUtil(GzipParameters parameters) throws IOException {
        //this.out = out;
        this.deflater = new Deflater(parameters.getCompressionLevel(), true);
        
        writeHeader(parameters);
    }
    
    /**
     * 返回壓縮後的完整二進制
     * @return
     */
    public byte[] getCompressedByte() {
		return compressedByte;
	}
    
    
    /**
     * 頭文件寫文件,改爲返回頭文件二進制
     * @param parameters
     * @throws IOException
     */
    private void writeHeader(GzipParameters parameters) throws IOException {
        String filename = parameters.getFilename();
        String comment = parameters.getComment();
        
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        buffer.putShort((short) GZIPInputStream.GZIP_MAGIC);
        buffer.put((byte) Deflater.DEFLATED); // compression method (8: deflate)
        buffer.put((byte) ((filename != null ? FNAME : 0) | (comment != null ? FCOMMENT : 0))); // flags
        buffer.putInt((int) (parameters.getModificationTime() / 1000));
        
        // extra flags
        int compressionLevel = parameters.getCompressionLevel();
        if (compressionLevel == Deflater.BEST_COMPRESSION) {
            buffer.put((byte) 2);
        } else if (compressionLevel == Deflater.BEST_SPEED) {
            buffer.put((byte) 4);
        } else {
            buffer.put((byte) 0);
        }
        
        buffer.put((byte) parameters.getOperatingSystem());
        
        compressedByte = buffer.array();
        //out.write(compressedByte);
        
        if (filename != null) {
        	byte[] nameByte = filename.getBytes(CharsetNames.ISO_8859_1);
        	
        	ByteBuffer nbf = ByteBuffer.allocate(nameByte.length + 1);
        	
        	nbf.put(nameByte);
        	nbf.put((byte) 0);
        	
        	appendCompressedByte(nbf.array());
        	
            //out.write(filename.getBytes(CharsetNames.ISO_8859_1));
            //out.write(nbf.array());
        }
        
        if (comment != null) {
        	byte[] commentByte = comment.getBytes(CharsetNames.ISO_8859_1);
        	
        	ByteBuffer combf = ByteBuffer.allocate(commentByte.length + 1);
        	
        	combf.put(commentByte);
        	combf.put((byte) 0);
        	
        	appendCompressedByte(combf.array());
        	
            //out.write(combf.array());
        }
    }

    /** ===================================================================================================== **/
    
    @Override
    public void write(int b) throws IOException {
        write(new byte[]{(byte) (b & 0xff)}, 0, 1);
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.1
     */
    @Override
    public void write(byte[] buffer) throws IOException {
        write(buffer, 0, buffer.length);
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.1
     */
    @Override
    public void write(byte[] buffer, int offset, int length) throws IOException {
        if (deflater.finished()) {
            throw new IOException("Cannot write more data, the end of the compressed data stream has been reached");

        } else if (length > 0) {
            deflater.setInput(buffer, offset, length);
            
            while (!deflater.needsInput()) {
                deflate();
            }
            
            crc.update(buffer, offset, length);
        }
    }
    
    
    private void deflate() throws IOException {
        int length = deflater.deflate(deflateBuffer, 0, deflateBuffer.length);
        if (length > 0) {
        	
        	byte[] defByte = Arrays.copyOfRange(deflateBuffer, 0, length);
        	
        	appendCompressedByte(defByte);
        	
            //out.write(defByte);
        }
    }
    
    
    private void writeTrailer() throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        buffer.putInt((int) crc.getValue());
        buffer.putInt(deflater.getTotalIn());

        byte[] traByte = buffer.array();
        
        appendCompressedByte(traByte);
        //out.write(traByte);
    }

    

    /**
     * Finishes writing compressed data to the underlying stream without closing it.
     * 
     * @since 1.7
     */
    public void finish() throws IOException {
        if (!deflater.finished()) {
            deflater.finish();

            while (!deflater.finished()) {
                deflate();
            }
            
            writeTrailer();
        }
    }
    
    @Override
    public void close() throws IOException {
        if (!closed) {
            finish();
            deflater.end();
            //out.close();
            closed = true;
        }
    }
    
    /**
     * 追加壓縮後的數組
     * @param append
     */
	public void appendCompressedByte(byte[] append){
		int originalLength = compressedByte.length;
		int appendLength = append.length;
		//先擴容長度
		int totalLength = originalLength + appendLength;
		
		compressedByte = Arrays.copyOf(compressedByte, totalLength);
		
		System.arraycopy(append, 0, compressedByte, originalLength, appendLength);
		
	}

}

使用方法:

	/**
	 * 文件壓縮
	 */
	public static void copyTest(){
		String pathFrom = "D:/testFile/compress/compress.txt";
		
		String pathTo = "D:/testFile/compress/compress.txt.gz";
		
		try {
			
			GzipParameters parameters = new GzipParameters();
			
			parameters.setFilename("test");
			parameters.setCompressionLevel(5);
			
			
			InputStream input = new FileInputStream(pathFrom);
			OutputStream output = new FileOutputStream(pathTo);
			
			GzipByteUtil gzipOut = new GzipByteUtil(parameters);
			
			byte[] readByte = new byte[1024 * 4];
			
			int length;
			while((length = input.read(readByte)) != -1){
				gzipOut.write(readByte, 0, length);
				
				gzipOut.flush();
			}
			
			gzipOut.close();
			
			//獲取壓縮二進制
			byte[] resultByte = gzipOut.getCompressedByte();
			
			output.write(resultByte);
			
			System.out.println(resultByte.length);
			
			output.close();
			input.close();
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章