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