压缩是编程中常见的技巧,多用于大文件压缩,数据流压缩等。在Java类库中,内置了jar、ZIP、GZIP、ZLIB等的支持(见java.util.zip、java.util.jar包)。另外在Apache项目下Ant中ant.jar的org.apache.tools.tar、org.apache.tools.zip、org.apache.tools.bzip2分别提供了tar、zip、bzip2的支持;Apache commons compress项目里提供了对AR、BZIP、CPIO、GZP、TAR、ZIP的支持。7-zip提供了LZMA格式的压缩(public domain),QuickLZ提供了quicklz格式的压缩(GPL),oberhumer.com提供LZO格式的压缩(GPL),hadoop-gpl-compression则对LZO的c实现用JNI进行包装,提供更快捷的LZO压缩。
严格来说,TAR、AR、CPIO并不属于压缩软件,而是一种打包软件,它能把很多文件、文件夹打包成一个文件,供压缩程序压缩。而咱们在windows中熟悉的zip、rar,严格的说是具备打包和压缩功能的一种格式。
因为本人在项目中需主要需要应用压缩工具对网络中传输的数据流进行压缩,因此重点关心对Stream的压缩,而不关心对多个文件的压缩,这在代码中也会有所体现。因此,本系列的代码一般仅适用于压缩流或压缩一个文件。
LZMA、QuickLZ、LZO因为提供的类库不支持stream形式压缩或提供的example太难看懂,故不作测试。这里还有一个需要提醒,Apache commons compress的tar、zip、bzip2来最初源于ant,但经过项目间迁移、演化,API及性能有所不同。
所有的格式均提供压缩和解压两个方法,再次提醒这里所有代码不适用于压缩多个文件、文件夹。下面是抽象的压缩、加压缩类:package study.inkfish.compress; import java.io.File; import java.io.IOException; public abstract class Compress { public void compress(File srcFile, File destFile) { destFile.getParentFile().mkdirs(); try { doCompress(srcFile, destFile); } catch (IOException ex) { ex.printStackTrace(); } } public void decompress(File srcFile, File destDir) { destDir.mkdirs(); try { doDecompress(srcFile, destDir); } catch (IOException ex) { ex.printStackTrace(); } } protected int bufferLen = 1024 * 1024;//buffer size: 1MByte protected abstract void doCompress(File srcFile, File destFile) throws IOException; protected abstract void doDecompress(File srcFile, File destDir) throws IOException; }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; /** JDK ZLIB压缩: */ public class JdkZLIBCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { OutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new DeflaterOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); IOUtils.copy(is, out); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { InputStream is = null; OutputStream os = null; try { File destFile = new File(destDir, FilenameUtils.getBaseName(srcFile.toString())); is = new InflaterInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); os = new BufferedOutputStream(new FileOutputStream(destFile), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); } } }JDK ZIP压缩(仅适用于压缩一个文件):
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; public class JdkZipCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { ZipOutputStream zout = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); zout = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); zout.putNextEntry(new ZipEntry(srcFile.getName())); IOUtils.copy(is, zout); zout.closeEntry(); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(zout); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { ZipInputStream is = null; try { is = new ZipInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); ZipEntry entry = null; while ((entry = is.getNextEntry()) != null) { if (entry.isDirectory()) { File directory = new File(destDir, entry.getName()); directory.mkdirs(); is.closeEntry(); } else { OutputStream os = null; try { os = new BufferedOutputStream( new FileOutputStream(new File(destDir, entry.getName())), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(os); } is.closeEntry(); } } } finally { IOUtils.closeQuietly(is); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; public class JdkGZIPCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { OutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); IOUtils.copy(is, out); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { InputStream is = null; OutputStream os = null; try { File destFile = new File(destDir, FilenameUtils.getBaseName(srcFile.toString())); is = new GZIPInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); os = new BufferedOutputStream(new FileOutputStream(destFile), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); } } }注:org.apache.commons.io包为Apache common io,项目首页: http://commons.apache.org/io/,提供了IO操作的很多方便的方法,基于Apache 2.0 License,可用于商业用途。
Ant ZIP压缩:
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import org.apache.commons.io.IOUtils; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; import org.apache.tools.zip.ZipOutputStream; public class AntZipCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { ZipOutputStream zout = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); zout = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); zout.putNextEntry(new ZipEntry(srcFile.getName())); IOUtils.copy(is, zout); zout.closeEntry(); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(zout); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { ZipFile zipFile = new ZipFile(srcFile); try { @SuppressWarnings("unchecked") Enumeration<ZipEntry> enums = zipFile.getEntries(); while (enums.hasMoreElements()) { ZipEntry entry = enums.nextElement(); InputStream is = new BufferedInputStream(zipFile.getInputStream(entry), bufferLen); OutputStream os = null; try { os = new BufferedOutputStream(new FileOutputStream(new File(destDir, entry.getName())), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); } } } finally { ZipFile.closeQuietly(zipFile); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.tools.bzip2.CBZip2InputStream; import org.apache.tools.bzip2.CBZip2OutputStream; public class AntBzip2Compress extends Compress { /**运行异常,无法正确打开*/ @Override protected void doCompress(File srcFile, File destFile) throws IOException { OutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new CBZip2OutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); IOUtils.copy(is, out); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { InputStream is = null; OutputStream os = null; try { File destFile = new File(destDir, FilenameUtils.getBaseName(srcFile.toString())); is = new CBZip2InputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); os = new BufferedOutputStream(new FileOutputStream(destFile), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; import org.apache.tools.tar.TarEntry; import org.apache.tools.tar.TarInputStream; import org.apache.tools.tar.TarOutputStream; public class AntTarCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { TarOutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); TarEntry entry = new TarEntry(srcFile.getName()); entry.setSize(srcFile.length()); out.putNextEntry(entry); IOUtils.copy(is, out); out.closeEntry(); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { TarInputStream is = null; try { is = new TarInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); TarEntry entry = null; while ((entry = is.getNextEntry()) != null) { if (entry.isDirectory()) { File directory = new File(destDir, entry.getName()); directory.mkdirs(); } else { BufferedOutputStream bos = null; try { byte[] buffer = new byte[1024 * 512];//512k buffer bos = new BufferedOutputStream(new FileOutputStream( new File(destDir, entry.getName())), buffer.length); int len; long size = entry.getSize(); while (size > 0) { if (size < buffer.length) { len = is.read(buffer, 0, (int) size); size -= len; } else { len = is.read(buffer); size -= len; } bos.write(buffer, 0, len); } } finally { IOUtils.closeQuietly(bos); } } } } finally { IOUtils.closeQuietly(is); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; public class CommonsBZip2Compress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { OutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new GzipCompressorOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); IOUtils.copy(is, out); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { InputStream is = null; OutputStream os = null; try { File destFile = new File(destDir, FilenameUtils.getBaseName(srcFile.toString())); is = new GzipCompressorInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); os = new BufferedOutputStream(new FileOutputStream(destFile), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; public class CommonsGZIPCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { OutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new BZip2CompressorOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); IOUtils.copy(is, out); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { InputStream is = null; OutputStream os = null; try { File destFile = new File(destDir, FilenameUtils.getBaseName(srcFile.toString())); is = new BZip2CompressorInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); os = new BufferedOutputStream(new FileOutputStream(destFile), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.io.IOUtils; public class CommonsZipCompress extends Compress { /**用于单文件压缩*/ @Override protected void doCompress(File srcFile, File destFile) throws IOException { ZipArchiveOutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new ZipArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); ZipArchiveEntry entry = new ZipArchiveEntry(srcFile.getName()); entry.setSize(srcFile.length()); out.putArchiveEntry(entry); IOUtils.copy(is, out); out.closeArchiveEntry(); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { ZipArchiveInputStream is = null; try { is = new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); ZipArchiveEntry entry = null; while ((entry = is.getNextZipEntry()) != null) { if (entry.isDirectory()) { File directory = new File(destDir, entry.getName()); directory.mkdirs(); } else { OutputStream os = null; try { os = new BufferedOutputStream( new FileOutputStream(new File(destDir, entry.getName())), bufferLen); IOUtils.copy(is, os); } finally { IOUtils.closeQuietly(os); } } } } finally { IOUtils.closeQuietly(is); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.compress.archivers.ar.ArArchiveEntry; import org.apache.commons.compress.archivers.ar.ArArchiveInputStream; import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream; import org.apache.commons.io.IOUtils; public class CommonsArCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { ArArchiveOutputStream zout = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); zout = new ArArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); zout.putArchiveEntry(new ArArchiveEntry(srcFile, srcFile.getName())); IOUtils.copy(is, zout); zout.closeArchiveEntry(); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(zout); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { ArArchiveInputStream is = null; try { is = new ArArchiveInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); ArArchiveEntry entry = null; while ((entry = is.getNextArEntry()) != null) { if (entry.isDirectory()) { File directory = new File(destDir, entry.getName()); directory.mkdirs(); } else { BufferedOutputStream bos = null; try { byte[] buffer = new byte[bufferLen]; bos = new BufferedOutputStream(new FileOutputStream( new File(destDir, entry.getName())), bufferLen); int len; long size = entry.getSize(); while (size > 0) { if (size < bufferLen) { len = is.read(buffer, 0, (int) size); size -= len; } else { len = is.read(buffer); size -= len; } bos.write(buffer, 0, len); } } finally { IOUtils.closeQuietly(bos); } } } } finally { IOUtils.closeQuietly(is); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.compress.archivers.cpio.CpioArchiveEntry; import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream; import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream; import org.apache.commons.io.IOUtils; public class CommonsCPIOCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { CpioArchiveOutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new CpioArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); out.putArchiveEntry(new CpioArchiveEntry(srcFile, srcFile.getName())); IOUtils.copy(is, out); out.closeArchiveEntry(); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { CpioArchiveInputStream is = null; try { is = new CpioArchiveInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); CpioArchiveEntry entry = null; while ((entry = is.getNextCPIOEntry()) != null) { if (entry.isDirectory()) { File directory = new File(destDir, entry.getName()); directory.mkdirs(); } else { BufferedOutputStream bos = null; try { byte[] buffer = new byte[bufferLen]; bos = new BufferedOutputStream(new FileOutputStream( new File(destDir, entry.getName())), bufferLen); int len; long size = entry.getSize(); while (size > 0) { if (size < bufferLen) { len = is.read(buffer, 0, (int) size); size -= len; } else { len = is.read(buffer); size -= len; } bos.write(buffer, 0, len); } } finally { IOUtils.closeQuietly(bos); } } } } finally { IOUtils.closeQuietly(is); } } }
package study.inkfish.compress; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.io.IOUtils; public class CommonsTarCompress extends Compress { @Override protected void doCompress(File srcFile, File destFile) throws IOException { TarArchiveOutputStream out = null; InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(srcFile), bufferLen); out = new TarArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(destFile), bufferLen)); TarArchiveEntry entry = new TarArchiveEntry(srcFile.getName()); entry.setSize(srcFile.length()); out.putArchiveEntry(entry); IOUtils.copy(is, out); out.closeArchiveEntry(); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(out); } } @Override protected void doDecompress(File srcFile, File destDir) throws IOException { TarArchiveInputStream is = null; try { is = new TarArchiveInputStream(new BufferedInputStream(new FileInputStream(srcFile), bufferLen)); TarArchiveEntry entry = null; while ((entry = is.getNextTarEntry()) != null) { if (entry.isDirectory()) { File directory = new File(destDir, entry.getName()); directory.mkdirs(); } else { BufferedOutputStream bos = null; try { byte[] buffer = new byte[bufferLen]; bos = new BufferedOutputStream(new FileOutputStream( new File(destDir, entry.getName())), bufferLen); int len; long size = entry.getSize(); while (size > 0) { if (size < bufferLen) { len = is.read(buffer, 0, (int) size); size -= len; } else { len = is.read(buffer); size -= len; } bos.write(buffer, 0, len); } } finally { IOUtils.closeQuietly(bos); } } } } finally { IOUtils.closeQuietly(is); } } }