###引言
  在做项目的时候经常会涉及到文件的压缩,比如近期用Java Web做一个后台管理,后台有个导出功能,需要统计生成几十个excel文件,然后进行下载,如果不将这些文件进行压缩传送,耗费用户流量不说,用户浏览器还会一个接一个地接收文件不停的点击确认保存。所以需要对文件进行压缩传送。
  关于文件压缩,Java的java.util.zip包提供了这个功能。这个包下总共有20多个相关的类,下面介绍一下常用的几个类,读者感兴趣也可查看该工具包。
  该工具包下和解压缩相关的基类有ZipInputStream、ZipOutputStream、GZIPInputStream、GZIPOutputStream。其中ZipInputStream、GZIPInputStream这两个输入流主要用于解压时读取压缩文件,ZipOutputStream、GZIPOutputStream两个输出流用于压缩,将数据流写入压缩文件。通过名字我们可以看到这两个类对应我们常见的以.zip和.gzip结尾的压缩文件类型。另外还有一个ZipFile,这个类会在解压时用到。个人理解是用于初始化解析File。
  关于压缩,首先贴上实现的压缩代码

/*** Created by someone on 2017/10/16.*/
public class IOUtil {/*** 通过指定路径和文件名来获取文件对象,当文件不存在时自动创建* @param path* @param fileName* @return* @throws IOException*/public static File getFile(String path, String fileName) throws IOException {// 创建文件对象File file;if (path != null && !path.equals(""))file = new File(path, fileName);elsefile = new File(fileName);if (!file.exists()) {file.createNewFile();}// 返回文件return file;}/*** 获得指定文件的输出流* @param file* @return* @throws FileNotFoundException*/public static FileOutputStream getFileStream(File file) throws FileNotFoundException {return new FileOutputStream(file);}/*** 将多个文件压缩* @param fileList 待压缩的文件列表* @param zipFileName 压缩文件名* @return 返回压缩好的文件* @throws IOException*/public static File getZipFile(List<File> fileList, String zipFileName) throws IOException {File zipFile = getFile(PathConfig.getPath(), zipFileName);// 文件输出流FileOutputStream outputStream = getFileStream(zipFile);// 压缩流ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);int size = fileList.size();// 压缩列表中的文件for (int i = 0;i < size;i++) {File file = fileList.get(i);zipFile(file, zipOutputStream);}// 关闭压缩流、文件流zipOutputStream.close();outputStream.close();return zipFile;}/*** 将文件数据写入文件压缩流* @param file 带压缩文件* @param zipOutputStream 压缩文件流* @throws IOException*/private static void zipFile(File file, ZipOutputStream zipOutputStream) throws IOException {if (file.exists()) {if (file.isFile()) {FileInputStream fis = new FileInputStream(file);BufferedInputStream bis = new BufferedInputStream(fis);ZipEntry entry = new ZipEntry(file.getName());zipOutputStream.putNextEntry(entry);final int MAX_BYTE = 10 * 1024 * 1024; // 最大流为10MBlong streamTotal = 0; // 接收流的容量int streamNum = 0; // 需要分开的流数目int leaveByte = 0; // 文件剩下的字符数byte[] buffer; // byte数据接受文件的数据streamTotal = bis.available(); // 获取流的最大字符数streamNum = (int) Math.floor(streamTotal / MAX_BYTE);leaveByte = (int) (streamTotal % MAX_BYTE);if (streamNum > 0) {for (int i = 0;i < streamNum;i++) {buffer = new byte[MAX_BYTE];bis.read(buffer, 0, MAX_BYTE);zipOutputStream.write(buffer, 0, MAX_BYTE);}}// 写入剩下的流数据buffer = new byte[leaveByte];bis.read(buffer, 0, leaveByte); // 读入流zipOutputStream.write(buffer, 0, leaveByte); // 写入流zipOutputStream.closeEntry(); // 关闭当前的zip entry// 关闭输入流bis.close();fis.close();}}}
}

  代码比较好懂,就是循环遍历待压缩文件列表,将每个文件写入到压缩流。

  主要看下private static void zipFile(File file, ZipOutputStream zipOutputStream) throws IOException这个压缩的核心代码方法签名。可以看到在该方法中我们首先实例化了一个ZipEntry 对象,之后就是将zipEntry对象作为参数并调用了ZipOutputStream的putNextEntry()方法,接下来读取File的数据流并写入压缩流,通过循环将数据流写入压缩流完毕后,就调用了zipOutputStream的closeEntry()方法关闭当前的ZipEntry。

  用Java实现文件压缩功能到此就实现完毕了,很简单。但是这里我比较好奇究竟是怎么进行压缩的,于是就寻着ZipOutputStream的方法调用去查个究竟。下面是ZipOutputStream的代码实现,putNextEntry方法和write方法:

/*** 开始一个新的Zip入口。如果当前入口是打开状态则关闭。把新入口作为当前的压缩流入口* 如果没有指定压缩方法,则会使用默认的压缩方法。* 如果没有指定文件修改时间,则会使用系统当前时间。* @param e 将要写入压缩流的入口* @exception ZipException 如果发生了压缩错误抛出此异常* @exception IOException 如果发生了IO错误抛出此异常*/public void putNextEntry(ZipEntry e) throws IOException {ensureOpen(); // 确保压缩流为打开状态if (current != null) {closeEntry();       // 关闭之前的入口}if (e.xdostime == -1) {// 如果没有文件修改时间,则使用当前系统时间e.setTime(System.currentTimeMillis());}if (e.method == -1) {e.method = method;  // 使用默认压缩方法,有DEFAULT和STORED两种}// 存储大小, 压缩后大小, crc-32 in LOC headere.flag = 0;switch (e.method) {case DEFLATED:// store size, compressed size, and crc-32 in data descriptor// immediately following the compressed entry dataif (e.size  == -1 || e.csize == -1 || e.crc   == -1)e.flag = 8;break;case STORED:// 使用STORED压缩方法,压缩后大小、压缩前大小和crc-32一定被设置过。if (e.size == -1) {e.size = e.csize;} else if (e.csize == -1) {e.csize = e.size;} else if (e.size != e.csize) {throw new ZipException("STORED entry where compressed != uncompressed size");}if (e.size == -1 || e.crc == -1) {throw new ZipException("STORED entry missing size, compressed size, or crc-32");}break;default:throw new ZipException("unsupported compression method");}if (! names.add(e.name)) {throw new ZipException("duplicate entry: " + e.name);}if (zc.isUTF8())e.flag |= EFS;current = new XEntry(e, written); // 使用XEntry将ZipEntry包裹xentries.add(current); // 将该入口添加到Vector<XEntry>writeLOC(current); // 添加头部信息}/*** 将byte数组写到入口,即将从数据流读到的数据写入压缩流* @param b byte数组* @param off byte数组的偏移* @param len 要写入的大小* @exception ZipException if a ZIP file error has occurred* @exception IOException if an I/O error has occurred*/public synchronized void write(byte[] b, int off, int len)throws IOException{ensureOpen();if (off < 0 || len < 0 || off > b.length - len) {throw new IndexOutOfBoundsException();} else if (len == 0) {return;}if (current == null) {throw new ZipException("no current ZIP entry");}ZipEntry entry = current.entry;switch (entry.method) {case DEFLATED: // 默认是DEFLATED,所以会首先调用父类的write方法。super.write(b, off, len);break;case STORED:// 如果数据本来是压缩的,那么就将拿到的数据流直接写到压缩流了written += len;if (written - locoff > entry.size) {throw new ZipException("attempt to write past end of STORED entry");}out.write(b, off, len);break;default:throw new ZipException("invalid compression method");}crc.update(b, off, len);}

  从上边的write方法中我们看到其实是调用了父类DeflaterOutputStream的write方法:

    public void write(byte[] b, int off, int len) throws IOException {if (def.finished()) {throw new IOException("write beyond end of stream");}if ((off | len | (off + len) | (b.length - (off + len))) < 0) {throw new IndexOutOfBoundsException();} else if (len == 0) {return;}if (!def.finished()) { // 判断已经压缩的数据输出流是否结束def.setInput(b, off, len); // 设置压缩需要的输入数据,调用该方法其实并没有 做什么事情,只是简单的进行赋值while (!def.needsInput()) { // 循环判断是否需要输入数据,根据len的值是否小于等于0.deflate(); // 这个才是压缩的核心方法}}}

通过以上代码,我们看到deflate()方法才是压缩的核心代码,该方法最终会通过JNI本地调用deflateBytes方法来进行压缩,该方法或将压缩后的字节大小返回,因为数据流byte是引用类型,所以通过该压缩方法处理后会变成压缩后的数据,有了压缩后的数据以及大小,接下来就可以通过out.write(buf, 0, len);将压缩后的数据写入压缩流了。所以关于数据压缩的核心代码在Java中是看不到了~

使用Java实现多个文件压缩打包相关推荐

  1. dd命令打包多个文件_linux的tar命令详情;linux多个文件压缩打包到一个压缩文件...

    tar命令 可以用来压缩打包单文件.多个文件.单个目录.多个目录. Linux打包命令_tar tar命令可以用来压缩打包单文件.多个文件.单个目录.多个目录. 常用格式: 单个文件压缩打包 tar ...

  2. linux多个文件打包命令行,linux命令五十七之tar命令;linux多个文件压缩打包到一个压缩文件...

    tar命令linux 能够用来压缩打包单文件.多个文件.单个目录.多个目录.shell Linux打包命令_tarspa tar命令能够用来压缩打包单文件.多个文件.单个目录.多个目录..net 经常 ...

  3. java实现对pdf文件压缩,拆分,修改水印,添加水印

    最近要实现一个文件上传,并且在线预览上传文件的功能,设计思路是:把上传的文件通过openoffice转成pdf文件,并将pdf文件以流的形式返回到浏览器,由于上传的部分文件过大,转成pdf后传回前端浏 ...

  4. 压缩命令_Linux环境下文件压缩打包命令详解

    你好,我是goldsunC 让我们一起进步吧! 前言 我们知道,在面向对象的程序设计中,一切皆对象.而在Linux操作系统中,一切皆文件,因此我们总会跟文件打交道. Linux文件系统很庞大复杂,不过 ...

  5. java编程笔记18 文件压缩与解压缩

    在java中,主要是利用ZipEntry,ZipInputStream和ZipOutputStream来实现zip数据压缩方式的编程方法, 构造方法摘要 ZipEntry(String name)   ...

  6. Linux基础知识与实操-篇三: 文件压缩打包与vim基本使用

    文章目录 压缩打包与备份 压缩文件命令`gzip bzip2 xz` `gzip` 命令 `bzip2` 命令 `xz`命令 打包指令 XFS文件系统备份与还原 光盘写入工具 其他常见的压缩与备份工具 ...

  7. linux的文件压缩打包操作,Linux文件管理-压缩打包

    原标题:Linux文件管理-压缩打包 压缩打包介绍 windows下我们接触最多的压缩文件就是.rar格式, 但Linux有自己所特有的压缩工具. 如果希望windows和Linux互相能使用的压缩工 ...

  8. 批量下载,多文件压缩打包zip下载

    0.写在前面的话 图片批量下载,要求下载时集成为一个压缩包进行下载.从昨天下午折腾到现在,踩坑踩得莫名其妙,还是来唠唠,给自己留个印象的同时,也希望给需要用到这个方法的人带来一些帮助. 1.先叨叨IO ...

  9. Java根据多个文件URL打包成一个压缩包下载

    直接上代码: @GetMapping("/download")public void downloadFiles(HttpServletRequest request, HttpS ...

  10. Java使用zip4j进行文件压缩

    pom依赖: <!-- https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j --> <dependency>& ...

最新文章

  1. 【Android 高性能音频】Oboe 函数库简介 ( Oboe 简介 | Oboe 特点 | Oboe 编译工具 | Oboe 相关文档 | Oboe 测试工具 )
  2. Javascript数字前补零的功能
  3. 产品经理不得不知的APP数据分析及报表设计基础
  4. redis延迟队列 如何确保成功消费_千万级延时任务队列如何实现,看美图开源的-LMSTFY...
  5. mac终端命令行总结
  6. 关于Linux的总结(三)
  7. 信息学奥赛一本通(1405:质数的和与积)
  8. php网站用框架与不用的区别,做前端网页是不是必须要用网页框架
  9. Exchange Server2010系列之二:部署三合一角色(CAS+HT+MBX)
  10. 计算机如何去除桌面名称阴影,去除桌面图标阴影 - 桌面图标有蓝色阴影怎么去掉 - 安全专题...
  11. 看漫画学焊接!5分钟教你电烙铁的焊接方法
  12. 举一反三的贝壳网爬虫
  13. mysql未开启binlog恢复_mysql 开启binlog,并恢复数据操作
  14. w10计算机运行特别卡,win10会很卡,详细教您怎么解决
  15. Twincat3之C++
  16. Mac book笔记本输入法错乱
  17. java list clear 垃圾回收_Java垃圾回收
  18. 服务器能当电脑用吗?与普通电脑有何区别?
  19. Hdu 5873 2016 ACM/ICPC Asia Regional Dalian Online 1006(兰道定理)
  20. 休假真好,不想上班了

热门文章

  1. 使用Python进行数独求解(一)
  2. matlab get,matlab中的get函数怎么用?
  3. ie浏览器点击打印没反应_解决在IE菜单中点击打印无反应
  4. dsolve解微分方程
  5. shanzhi -接小球游戏2.0
  6. html怎么隐藏项目符号,CSS-如何隐藏侧边栏列表中的项目符号?
  7. 主打python-2021年度总结-展望2022年
  8. ATmega16 单片机 AVR单片机 智能风扇控制器
  9. 电脑删除的文件去哪了?删除的数据恢复,居然有3个方法…
  10. 微信小程序性能优化实用建议