Java的zip包主要提供了压缩和解压算法,主要包括zip, gzip, and inflate/deflate等格式。

一、

Deflate/Inflate、Zlib和GZip

1.

原生deflate

原生压缩deflate是一种压缩算法,主要使用了LZ77和霍夫曼进行编码,这些压缩算法都是由C编写。参见前文《24-数据压缩之LZ77算法》http://blog.sina.com.cn/s/blog_7d1968e20102xjam.html

1)

保存格式Zlib和GZip

使用Deflate压缩后默认的保存格式就是Zlib,使用zlib库压缩后的数据会在deflate数据的头和尾添加信息,形成zlib格式的数据(rfc1951)。

gzip也是一种数据压缩格式,可以大体分为头部,数据部和尾部三个部分,其中头部和尾部主要是一些文档属性和校验信息(rfc1952),数据部主要是用deflate方法压缩得到的数据。

在Deflator创建时可以设置保存格式是否为Zlib或GZip。

2)

压缩等级

压缩等级范围从0-9,0代表不压缩,9代表最大压缩。通常来说,压缩等级越高,压缩后的字节越小,但所需时间越长。以下为常见压缩等级参数:

public static final int NO_COMPRESSION =

0;

public static final int BEST_SPEED = 1;

public static final int BEST_COMPRESSION =

9;

public static final int DEFAULT_COMPRESSION =

-1;

3)

压缩策略

Deflate的压缩策略如下定义:

public static final int DEFAULT_STRATEGY =

0;

public static final int FILTERED = 1;

public static final int HUFFMAN_ONLY =

2;

比较常见的压缩方式为霍夫曼树。

参见《算法-二叉树2-霍夫曼编码》http://blog.sina.com.cn/s/blog_7d1968e20102x0lo.html

4)

设置预设字典

压缩过程就是将当前文件中的各个字符转化为压缩字典过程,最明显的例子就是霍夫曼编码过程。

当然我们这里的Deflate算法默认采用LZ77压缩算法。压缩时首先需要这个字符存在于字典(缓存)中,一旦字典中没有这个字符,那么其压缩性能就远不如那些存在于字典的字符了。所以压缩流前面部分的字符可能没有后面那些字符压缩的那么好(当然,霍夫曼编码不是这样的过程,霍夫曼编码要求通读整个数据文件,然后才能编排字典)。而这就是预设字典的作用,对一些字符进行预设编码,从而提高压缩性能,但一般不推荐使用预设字典,容易玩蹦。

因此,在压缩文件中不支持随机访问,因为数据被压缩,单个数据无法解压;同时,一旦压缩文件中某个字符被修改,可能会影响到全局解压。

5)

Deflator循环判断needsInput

如果Deflator.deflate() 返回0,那么就需要检查needsInput()来判断是否继续压缩数据setInput()。

在最终输入流耗尽时,就可以调用finish停止压缩。当然如果想使用同样压缩Deflator继续压缩其他文件,使用reset方法。

6)

使用

流程图如下:

package com.scn7th.compress;

import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.util.List;

import java.util.zip.Deflater;@Slf4jpublic class FirstDeflate {

public static void run(ListString

fileNames) {

Deflater deflater = new Deflater();

deflater.setStrategy(Deflater.DEFAULT_STRATEGY);

for(String fileName : fileNames) {

try (FileInputStream fileInputStream =

new FileInputStream("file/" + fileName + ".txt");

FileOutputStream fileOutputStream =

new FileOutputStream("file/deflate/" + fileName + ".dfl");

){

byte[] input = new byte[1024];

byte[] output = new byte[1024];

while (true) {

int read =

fileInputStream.read(input);

if(read == -1) {

deflater.finish();

log.info("file {} compressed total:{}", fileName, deflater.getTotalOut());

// Deflate any data that remains in the input

buffer.while (!deflater.finished()) {

int compressedBytes =

deflater.deflate(output);

if(compressedBytes

0) {

fileOutputStream.write(output, 0, compressedBytes);

}

break;

}

break;

}

else {

deflater.setInput(input, 0 ,read);

//此处循环原因可能对于input进行压缩,一次output放不下,所以需要多次然后输出到out

while (!deflater.needsInput()) {

int compressedBytes =

deflater.deflate(output);

if(compressedBytes

0) {

fileOutputStream.write(output, 0, compressedBytes);

}

}

log.info("file {} compressed so far:{} and total in

{}", fileName, deflater.getTotalOut(), deflater.getTotalIn());

}

}

deflater.reset();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

2.

原生inflate

1)

基本方法和inflate判断结束讨论

Inflator具有和Deflator类似的方法,也有setInput、reset、inflate等,只是没有了finish。

对于何时解压结束,Inflator并不是根据InputStream输入流结束来判断,而是根据自己的finished()方法,这个方法并没有与输入流关联,我猜想应该和LZ77压缩算法和Zlib压缩格式有关,Zlib中不仅包含了Adler校验等checksum校验,应该也包含了压缩前总字节总数等信息。一旦Inflator发现自己解压字节数达到了压缩文件中的总数,就将finish置为true,从而结束解压。

2)

使用

packagecom.scn7th.compress;

importlombok.extern.slf4j.Slf4j;

importjava.io.FileInputStream;

importjava.io.FileNotFoundException;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.util.List;

importjava.util.zip.DataFormatException;

importjava.util.zip.Inflater;@Slf4jpublic classFirstInflate {public static voidrun(ListString fileNames)throwsDataFormatException {

Inflater inflater =newInflater();

byte[] input =new byte[32];

byte[] output =new byte[32];

for(String fileName : fileNames) {try(FileInputStream fileInputStream =newFileInputStream("file/deflate/"+ fileName +".dfl");FileOutputStream fileOutputStream =newFileOutputStream("file/inflate/"+ fileName +".txt");) {while(true) {intread = fileInputStream.read(input);

if(read0) {

inflater.setInput(input,0,read);}else{log.info("input end");}intuncompressedBytes;

while((uncompressedBytes = inflater.inflate(output)) !=0) {if(uncompressedBytes0) {

fileOutputStream.write(output,0,uncompressedBytes);}

}log.info("file {} uncompressed total:{}",fileName,inflater.getTotalOut());//为何能判断结束?猜测可能是读取压缩文件头或者校验字段获取了压缩文件总长度,以此来判断是否解压完成if(inflater.finished()) {break;}else if(inflater.needsDictionary()) {throw newRuntimeException("需要字典!");}else if(inflater.needsInput()) {continue;}

}

inflater.reset();}catch(FileNotFoundException e) {

e.printStackTrace();}catch(IOException e) {

e.printStackTrace();}

}

}

}

3.

DeflateStream

deflaterOutputStream在处理多个filter流时不要直接close,而是调用finish。因为close会关闭依赖的流。

packagecom.scn7th.compress;

importcom.scn7th.util.StreamCopier;

importjava.io.FileInputStream;

importjava.io.FileNotFoundException;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.util.List;

importjava.util.zip.DeflaterOutputStream;

importjava.util.zip.GZIPOutputStream;public classDeflateStream {public static voiddeflate(ListString fileNames) {for(String fileName : fileNames) {try(FileInputStream fileInputStream =newFileInputStream("file/"+ fileName +".txt");FileOutputStream fileOutputStream =newFileOutputStream("file/deflate/"+ fileName +".dfl");DeflaterOutputStream deflaterOutputStream =newDeflaterOutputStream(fileOutputStream);) {

StreamCopier.copy(fileInputStream,deflaterOutputStream);deflaterOutputStream.finish();}catch(FileNotFoundException e) {

e.printStackTrace();}catch(IOException e) {

e.printStackTrace();}

}

}public static voidGZipDeflate(ListString fileNames) {for(String fileName : fileNames) {try(FileInputStream fileInputStream =newFileInputStream("file/"+ fileName +".txt");FileOutputStream fileOutputStream =newFileOutputStream("file/deflate/"+ fileName +".dfl");GZIPOutputStream gzipOutputStream =newGZIPOutputStream(fileOutputStream);) {

StreamCopier.copy(fileInputStream,gzipOutputStream);gzipOutputStream.finish();}catch(FileNotFoundException e) {

e.printStackTrace();}catch(IOException e) {

e.printStackTrace();}

}

}

}

4.

InflateStream

packagecom.scn7th.compress;

importcom.scn7th.util.StreamCopier;

importjava.io.FileInputStream;

importjava.io.FileNotFoundException;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.util.List;

importjava.util.zip.GZIPInputStream;

importjava.util.zip.InflaterInputStream;public classInflateStream {public static voidinflate(ListString fileNames) {for(String fileName : fileNames) {try(FileInputStream fileInputStream =newFileInputStream("file/deflate/"+ fileName +".dfl");FileOutputStream fileOutputStream =newFileOutputStream("file/inflate/"+ fileName +".txt");InflaterInputStream inflaterInputStream =newInflaterInputStream(fileInputStream);) {

StreamCopier.copy(inflaterInputStream,fileOutputStream);}catch(FileNotFoundException e) {

e.printStackTrace();}catch(IOException e) {

e.printStackTrace();}

}

}public static voidGZipInflate(ListString fileNames) {for(String fileName : fileNames) {try(FileInputStream fileInputStream =newFileInputStream("file/deflate/"+ fileName +".dfl");FileOutputStream fileOutputStream =newFileOutputStream("file/inflate/"+ fileName +".txt");GZIPInputStream gzipInputStream =newGZIPInputStream(fileInputStream);) {

StreamCopier.copy(gzipInputStream,fileOutputStream);}catch(FileNotFoundException e) {

e.printStackTrace();}catch(IOException e) {

e.printStackTrace();}

}

}

}

5.

Jdk1.7

Closable自动关闭

jdk1.7以后的新的资源回收机制,凡是实现了Closable接口的方法,在try中调用该资源,在执行结束后会由jvm自动调用Closable.close()方法,从而实现资源自动关闭

@Testpublic voidtestTryWithResources()throwsIOException {try(FileOutputStream fileOutputStream =newFileOutputStream("file/Copy.txt")) {

fileOutputStream.write(73);}

}

二、

Zip

Zip不仅仅是压缩方式,也是文件的一种存储方式。Zip包中不仅包含了压缩的文件,还包括一些头信息。这些描述被压缩文件的头信息称为zipEntry,他们包含了压缩方式(STORED/DIFLATED)、压缩文件大小、原文件大小、修改时间、CRC校验等信息。

1.

压缩ZipOutputStream

对于一个压缩ZipOutputStream,首先需要根据待压缩文件创建zipEntry,然后才开始压缩。每个zipEntry压缩完后,需要手动关闭ZipOutputStream.closeEntry,同时关闭输入流。

package com.scn7th.compress;

import com.scn7th.util.StreamCopier;

import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.util.List;

import java.util.zip.ZipEntry;

import java.util.zip.ZipOutputStream;@Slf4jpublic class MyZipOutputStream {

public void zip(String zippedFileName, ListString

zippingFileNames) {

try(ZipOutputStream zipOutputStream =

new ZipOutputStream(new FileOutputStream("file/zip/" + zippedFileName));)

{

for(String fileName : zippingFileNames) {

ZipEntry entry = new ZipEntry(fileName);

log.info("File name is : {}", fileName);

zipOutputStream.putNextEntry(entry);

FileInputStream inputStream = new FileInputStream("file/unzip/" + fileName);

StreamCopier.copy(inputStream, zipOutputStream);

zipOutputStream.closeEntry();

inputStream.close();

}

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

测试:

@Testpublic void zip() {

MyZipOutputStream zipOutputStream = new MyZipOutputStream();

ListString

zippingFileNames = new ArrayList();

zippingFileNames.add("Copy.txt");

zippingFileNames.add("ReadMe.txt");

zippingFileNames.add("utf-8.txt");

zipOutputStream.zip("firstZip.zip", zippingFileNames);}

2.

解压ZipInputStream

解压一个压缩文件,需要遍历其zipEntry,分别对每个文件进行解压输出。

package com.scn7th.compress;

import com.scn7th.util.StreamCopier;

import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.sql.Date;

import java.util.zip.ZipEntry;

import java.util.zip.ZipInputStream;@Slf4jpublic class MyZipInputStream {

public void unzip(String zippedFileName) {

try(ZipInputStream zipInputStream =

new ZipInputStream(new FileInputStream("file/zip/" + zippedFileName));) {

ZipEntry zipEntry = null;

while ((zipEntry = zipInputStream.getNextEntry())

!= null) {

FileOutputStream outputStream = new FileOutputStream("file/unzip/new" + zipEntry.getName());

log.info("Zipped file {} 's size is {} and original size is

{} and the last modify time is :{}",

zipEntry.getName(), zipEntry.getCompressedSize(), zipEntry.getSize(), new Date(zipEntry.getTime()));

StreamCopier.copy(zipInputStream, outputStream);

zipInputStream.closeEntry();

outputStream.close();

}

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

测试:

@Testpublic void unzip() {

MyZipInputStream zipInputStream = new MyZipInputStream();

zipInputStream.unzip("firstZip.zip");}

三、

校验算法

如何保证数据在传输以及压缩过程中不被篡改或者丢失是一个值得考虑的问题。《通信原理(樊昌信--第六版)》中详细介绍了各种数据传输校验的理论和方法,从最原始的奇偶校验,到汉明码,到循环码等等。Java io在进行解压和传输时也利用了其中的基本算法,主要包括Adler-32和循环冗余码CRC-32。

1.

Adler-32

Adler-32是Mark Adler发明的校验和算法,和32位CRC校验算法一样,都是保护数据防止意外更改的算法,但是这个算法较容易被伪造,所以是不安全的保护措施。但是比CRC好点的是,它计算的很快。这个算法那是从Fletcher校验和算法中修改过来的,原始的算法形式略快,但是可依赖性并不高。Adler-32的一种滚动哈希版本被用在了rsync工具中

Adler-32通过求解两个16位的数值A、B实现,并将结果连结成一个32位整数。

A就是字符串中每个字节的和,而B是A在相加时每一步的阶段值之和。在Adler-32开始运行时,A初始化为1,B初始化为0,最后的校验和要模上65521(继216之后的最小素数)。

A = 1 + D1

+ D2 + ... + Dn (mod 65521)

B = (1 +

D1) + (1 + D1 + D2) + ... + (1 + D1 + D2 + ... + Dn) (mod

65521)

= n×D1 + (n-1)×D2 +

(n-2)×D3 + ... + Dn + n (mod 65521)

Adler-32(D) = B × 65536 + A

其中D为字符串的字节,n是D的字节长度

参考

https://blog.csdn.net/liujiayu2/article/details/51685481

2.

CRC循环冗余校验码

关键证明和推导参见《通信原理(樊昌信--第六版)》340页。

也可参见《算法-数据压缩3-循环码校验CRC》http://blog.sina.com.cn/s/blog_7d1968e20102xjj6.html

3.

CheckedSumStream

package com.scn7th.compress;

import com.scn7th.util.StreamCopier;

import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.sql.Date;

import java.util.List;

import java.util.zip.*;@Slf4jpublic class MyCheckSumStream {

public void zip(String zippedFileName, ListString

zippingFileNames) {

try(ZipOutputStream zipOutputStream =

new ZipOutputStream(new FileOutputStream("file/zip/" + zippedFileName));

CheckedOutputStream checkedOutputStream =

new CheckedOutputStream(zipOutputStream, new CRC32());

)

{

for(String fileName : zippingFileNames) {

ZipEntry entry = new ZipEntry(fileName);

zipOutputStream.putNextEntry(entry);

FileInputStream inputStream = new FileInputStream("file/unzip/" + fileName);

StreamCopier.copy(inputStream, checkedOutputStream);

log.info("File name is : {} and check sum

is:{}", fileName, checkedOutputStream.getChecksum().getValue());

zipOutputStream.closeEntry();

inputStream.close();

}

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

public void unzip(String zippedFileName) {

try(ZipInputStream zipInputStream =

new ZipInputStream(new FileInputStream("file/zip/" + zippedFileName));

CheckedInputStream checkedInputStream =

new CheckedInputStream(zipInputStream, new CRC32());

) {

ZipEntry

zipEntry = null;

while ((zipEntry = zipInputStream.getNextEntry())

!= null) {

FileOutputStream outputStream = new FileOutputStream("file/unzip/new" + zipEntry.getName());

StreamCopier.copy(checkedInputStream, outputStream);

log.info("Zipped file {} 's size is {} and original size is

{} and the last modify time is :{} and check sum is

:{}",

zipEntry.getName(), zipEntry.getCompressedSize(), zipEntry.getSize(), new Date(zipEntry.getTime()), checkedInputStream.getChecksum().getValue());

zipInputStream.closeEntry();

outputStream.close();

}

} catch (FileNotFoundException e) {

e.printStackTrace();} catch (IOException e) {

e.printStackTrace();

}

}

}

测试:

package com.scn7th.compress;

import com.scn7th.JavaIoApplicationTests;

import org.junit.Test;

import java.util.ArrayList;

import java.util.List;public class CheckedSumTest extends JavaIoApplicationTests{

@Test

public void zip() {

MyCheckSumStream checkSumStream = new MyCheckSumStream();

ListString

zippingFileNames = new ArrayList();

zippingFileNames.add("Copy.txt");

zippingFileNames.add("ReadMe.txt");

zippingFileNames.add("utf-8.txt");

checkSumStream.zip("firstZip.zip", zippingFileNames);

}

@Test

public void unzip() {

MyCheckSumStream checkSumStream = new MyCheckSumStream();

checkSumStream.unzip("firstZip.zip");

}

}

java代表预设一个SQL_java-io基础-3-压缩和解压相关推荐

  1. java代码实现文件或文件夹的压缩和解压

    这里写了个工具类,可以实现文件的压缩和解压功能. package com.cntaiping.tpi.common.utils;import java.io.BufferedInputStream; ...

  2. 【Linux基础】压缩和解压

    Linux 常用的压缩与解压文件类型:.tar,.gz..tar.gz,.bz2..tar.bz2,.Z..tar.Z,.zip,.rar等. Linux 常用的压缩与解压缩命令有:tar,gzip. ...

  3. java 解压到内存,Java GZip 基于内存实现压缩和解压的方法

    欢迎大家关注本博,同时欢迎大家评论交流,可以给个赞哦!!! GZip是常用的无损压缩算法实现,在Linux中较为常见,像我们在Linux安装软件时,基本都是.tar.gz格式..tar.gz格式文件需 ...

  4. JAVA 7z Seven Zip 压缩和解压文件

    JAVA 7z Seven Zip 压缩和解压文件 7-Zip是基于GNU LGPL协议发布的软件,通过全新算法使压缩比率大幅提升 本文主要讲解通过JAVA方式把文件压缩成7z文件和对7z文件进行解压 ...

  5. 完美java.util.zip使用,实现zip压缩和解压

    之前找了很多demo代码在压缩中遇到各种问题,中文乱码.文本内容丢失等. 以下教程是本人亲自测试过,通过java.util.zip包实现java代码的文件zip压缩和解压: 1.压缩: 1)压缩一个文 ...

  6. java压缩和解压流,实现文件压缩和解压,代码都有注释

    压缩和解压流 压缩文件: ZipOutputStream 常用方法 方法名 介绍 ZipOutputStream(OutputStream out) 构造方法:创建新的ZIP输出流 public vo ...

  7. python猜数字游戏、在程序中预设一个_python 语法基础练习题

    python 语法基础练习题 1. 分别解释"=","==","+="的含义(口述) 2.两个变量值的关系?(口述) n1 = 123456 ...

  8. java zip 高效 解压,java实现zip的压缩和解压

    [ /** * 解压缩 * @param warPath 包地址 * @param unzipPath 解压后地址 */ public static void unzip(String warPath ...

  9. java 使用gzip压缩和解压 传输文件必备

    java gzip 压缩解压工具类,开箱即用 gzip原理看我另外一篇介绍 压缩效果直接看图: package com.yeahmobi.datacheck.util;import java.io.* ...

  10. java压缩和解压tar包,tar包、压缩与解压缩

    在Unix下常遇到一些解包.压包的问题.一般情况下将若干文件或文件夹打成tar包,是为了便于文件的传输和管理.例如,通过FTP从Unix平台下载目录结构较多的文件夹到Windows平台或另外一Unix ...

最新文章

  1. python中shutil模块_python文件、文件夹、压缩包处理模块-shutil模块-阿里云开发者社区...
  2. 重庆python就业工资待遇-重庆Python人工智能编程
  3. Saving Tang Monk II HihoCoder - 1828(2018北京网络赛三维标记+bfs)
  4. Debian on VirtualBox下共享win7文件夹设置
  5. [转]Windows 7 蓝屏后获取 MEMORY.DMP 文件及注意事项
  6. Linux 的 Shell 变量
  7. 节前福利:Java程序员面试宝典升级版
  8. Vue组件创建和组件传值
  9. 我如何学习:不要停下学习的脚步
  10. 数组存储地址的计算 --数据结构
  11. opencv换证件照底色
  12. Reactor3 Mono
  13. 泰拉瑞亚 服务器linux,Linux/CentOS7搭建泰拉瑞亚原版/mod服务器教程
  14. Anaconda 使用 set CONDA_FORCE_32BIT=1 切换32位环境失败的解决方法
  15. PWM 脉冲宽度调制
  16. 各种图论模型及其解答
  17. Faster R-CNN文章翻译——中英文对照
  18. win7/win10下装centos7双系统
  19. h5上下滑动动画效果(vue)
  20. scrap安装——Windows

热门文章

  1. java 状态常量_Java 变量和常量
  2. python docx table 边框_使用pythondocx指定表中的边框外观
  3. pythonbreak语句教程_Python break 语句
  4. 表单html遇到的问题及处理,HTML表单常见问题
  5. c++程序调用python代码_使用C++调用Python代码的方法详解
  6. pivot sqlserver 条件_SqlServer行转列(PIVOT),列转行(UNPIVOT)总结
  7. java编程显当前月示日历表_显示当前月的日历 1(java实现)
  8. 网络协议 21 - RPC 协议(中)- 基于 JSON 的 RESTful 接口协议
  9. python的下划线
  10. 如何将运维的报警做成运营的报警--Java后端架构