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